原文:Introduction to Unity Scripting
作者:Georgi Ivanov
译者:kmyhy

Unity 的许多功能都要通过它的富脚本语言 C# 来体现。你可以用它来处理用户输入,操作场景中的对象,碰撞检测,自动生成新的 GameObject 和在场景中发射定向光以处理游戏逻辑。听起来很可怕,但 Unity 提供了有良好文档的 API,使得这些任务的完成轻而易举——哪怕你是一个新手!

在本教程中,你将创建一个 Top down shooter 游戏,用 Unity 脚本处理敌人的生成、玩家控制、开火以及其它游戏中的重要方面。

注意:本教程假设你拥有一定的 C# 或类似编程语言经验,理解 Unity 的界面和工作方式。如果你忘记了这些内容,请阅读我们的 Unity 入门教程。

本教程针对 Unity 5.3 以上。你可以从这里下载 Unity 的最新版。

Unity 同时支持 UnityScript 和 Boo,但大部分程序员都喜欢使用 C#。C# 被数百万开发者用于开发 app、web 和游戏开发,有海量的资料和教程能够帮你学习它。

开始

下载开始项目 BlockBuster,解压缩,用 Unity 打开文件夹。

打开后是这个样子:

看一下场景视图。有一个小的竞技场,这是游戏的主战场,以及一个相机和一盏灯。如果你的布局和上图不同,请点击右上角的下拉框并选择 2 by 3。

主角都没有叫什么游戏?你的第一个任务就是创建一个代表玩家的 GameObject。

创建玩家角色

在结构视图中,点解 Create 按钮,选择 3D Object\Sphere。将球体放到 (X:0, Y:0.5, Z:0) 然后命名为 Player。

Unity 用全组件式系统来构建 GameObject。也就是说所有的 GameObject 都是由组件构成,这些组件会给游戏对象赋予行为和特性。这是几个 Unity 的内置组件:

  • Transforme: 每个 GameObject 都有这个组件。它保存了 GameObject 的位置、角度和比例。
  • Box Collider:一种立方形的碰撞体,用于检测碰撞。
  • Mesh Filter:用于显示 3D 模型的网格数据。

Player 游戏对象需要和场景中的其它对象发生碰撞反应。要实现这一点,请在结构视图中选择 Player,然后点击检视器窗口中的 Add Component 按钮。在弹出菜单中选择 Physics > Rigidbody,这就为 Player 添加了一个刚体组件,这样它就能够使用 Unity 的物理引擎了。

修改这个刚性体的属性为:Drag 设为 1,Angular Drag 为 0,勾选 Freeze Position 旁边的 Y。

这将保证玩家角色不会被上下移动,同时在转动时不添加阻尼系数。

编写玩家移动的脚本

玩家角色创建好之后,准备创建接收键盘输入以及移动玩家的脚本。

在项目窗口中,点击 Create Button\Folder。命名文件夹为 Scripts,然后在下面创建一个 Player 子文件夹。

在 Player 文件夹中,点击 Create 按钮,选择 C# Script。新脚本命名为 PlayerMovement。这个样子:

注意:创建这些文件夹有利于将文件安装各自的职能进行组织,避免混乱。你将为 Player 创建多个脚本,因此单独用一个文件夹会更好。

双击 PlayerMovement.cs 脚本。这会用你喜欢的代码编辑器打开这个脚本。Unity 内置了 MonoDevelop,它支持所有平台,在安装器运行时,Windows 用户可以安装 Visual Studio 来取代它。

本教程假设你使用 MonoDevelop,但 Visual Studo 用户也不会有任何问题。

当代码编辑器打开,你会看到:

这是 Unity 在新脚本中生成的默认的类。它继承了 MonoBehaviour 基类,这样脚本才能够在游戏中运行,同时还有一些特殊的方法对特定事件作出响应。如果你是一个 iOS 开发者,这个类就好比 UIViewCotnroller。Unity 会在运行脚本时以特定顺序调用多个方法。最常见的几个方法包括:

  • Start(): 这个方法在脚本第一次 update 时调用。
  • Update(): 当游戏正在运行,同时脚本是可用的,这个方法会在每帧刷新时调用。
  • OnDestroy(): 在这个脚本所附着的 GameObject 被销毁之前调用。
  • OnCollisionEnter(): 当这个脚本所附着的碰撞体或刚体和其它碰撞体或刚体发生接触时调用。

完整的事件列表,请参考 Unity 的 MonoBehaviours 文档。

在 Start() 方法前,添加两行代码:

public float acceleration;
public float maxSpeed;

脚本看起来是这个样子:

这是公共变量声明,这意味着这两个变量能够在检视器中看到并修改,而无需在脚本和编辑器中来回切换。

acceleration 表示玩家的速度随着时间递增。maxSpeed 则表示速度的上限。

在它们后面声明这几个变量:

private Rigidbody rigidBody;
private KeyCode[] inputKeys;
private Vector3[] directionsForKeys;

私有变量无法用检视器来设置,它的初始化由开发者在某个时机负责。

rigidBody 用于保存一个对刚体组件的引用,即附着在 Player GameObject 上的刚体组件。

inputKeys 是一个键盘码的数组,用于检查输入。

directionsForKeys 用于保存一个 Vector3 变量数组,这些变量表示方向数据。

将 Start() 方法修改为:

void Start () {inputKeys = new KeyCode[] { KeyCode.W, KeyCode.A, KeyCode.S, KeyCode.D };directionsForKeys = new Vector3[] { Vector3.forward, Vector3.left, Vector3.back, Vector3.right };rigidBody = GetComponent<Rigidbody>();
}

这段代码将按键对应到方向,比如 W 是向前。最后一行获得了一个对所附着的刚体组件的引用,将它保存到 rigidBody 变量以便使用。

要真正移动玩家的角色,还需要处理键盘输入。

将 Update() 修改为 FixedUpdate() 并加入以下代码:

// 1
void FixedUpdate () {for (int i = 0; i < inputKeys.Length; i++){var key = inputKeys[i];// 2if(Input.GetKey(key)) {// 3Vector3 movement = directionsForKeys[i] * acceleration * Time.deltaTime;}}
}

有几个地方需要注意一下:

  1. FixedUpdate() 是不依赖于帧率的,它和刚体一起使用。和以尽量快的速度运行不同,这个方法会以固定的间隔执行。
  2. 这个循环检测哪个按键被按下。
  3. 获取被按下的键所表示的方向,乘以加速度和上一帧耗费的秒数。最终算出一个方向向量(在 x,y 和 z 轴上的速度),你就可以用它来移动 Player 对象了。

如果你是编程新手,你可能奇怪为什么要乘以 Time.detalTime。游戏是在帧率(帧/秒)下运行的,帧率是取决于硬件和它运行压力,这样在性能好的机器上帧率快,而在性能差的机器上帧率慢,从而导致不可预知的结果。通常的办法是,当需要按每帧执行一个动作时,都乘上 Time.deltaTime。

在 FixedUpdate() 方法后添加:

void movePlayer(Vector3 movement) {if(rigidBody.velocity.magnitude * acceleration > maxSpeed) {rigidBody.AddForce(movement * -1);} else {rigidBody.AddForce(movement);}
}

这个方法向刚体施加一个力,驱使它移动。乳沟当前速度超过 maxSpeed,这个力会转成反方向,让玩家减速,将速度有效地限制在最大速度下。

在 FixedUpdate() 方法中,在 if 语句右括号结束之前,添加:

movePlayer(movement);

棒极了!保存脚本,回到 Unity 编辑器。在项目窗口,将 PlayerMovement 脚本拖到结构视图的 Player 上。

将脚本添加到一个 GameObject 会导致创建一个组件实例,也就是说所有的代码会被所附着的 GameObject 所执行。

用检视器将 Acceleration 设置为 625,Max Speed 设置为 4375:

运行场景,用 WASD 键移动玩家角色:

太好了,我们只用了几行代码!:]

但是,有一个很明显的问题——玩家会飞快地跑到视线以外,让我们很难去和敌人战斗啊。

编写相机脚本

在脚本编辑器中,新建脚本名为 CameraRig,然后将它添加到 Main Camera。还需要介绍详细步骤吗?你可以参考下面的答案。

参考步骤

选择 Scripts 文件夹,点击项目浏览器中的 Create 按钮,选择 C# 脚本。取名为 CameraRig。将新脚本拖到 Main Camera 对象:

然后,在新的 CameraRig 类的 Start() 方法之上中添加如下变量:

public float moveSpeed;
public GameObject target;private Transform rigTransform;

你可能想到了,moveSpeed 是相机跟随目标——任何场景内部游戏对象——进行移动的速度。

在 Start() 方法中,添加:

rigTransform = this.transform.parent;

这句引用了父对象 Camera 在场景树中的 transform 组件。每个在场景中的对象都会有一个 Transform 组件,它描述了对象的位置、角度和比例。

在同一脚本中,添加方法:

void FixedUpdate () {if(target == null){return;}rigTransform.position = Vector3.Lerp(rigTransform.position, target.transform.position, Time.deltaTime * moveSpeed);
}

CameraRig 的移动代码比 PlayerMovement 要简单。这是因为你不需要刚体,在 rigTransform 和 target 的位置之间做插值运算即可。

Vector3.Lerp() 以两个点和一个0-1 之间的小数做参数,这个小数表示两个端点之间的一个位置。左端点为 0,右端点为 1。0.5 则返回两点之间的终点。

以渐慢方式让 rigTransform 靠近 target 的位置。也就是说——相机会跟随玩家角色。

回到 Unity。在结构视图选中 Main Camera。在检视器中,设置 Move Speed 为 8 ,Target 为 Player:

运行游戏,在场景中四处移动;相机将平滑跟随 target。

创建敌人

没有对手的射击游戏玩起来固然轻松,但也太无聊了 :] 通过顶部 GameObject\3D Object\Cube 菜单创建一个方块作为敌人。将方块命名为 Enemy 并添加一个 Rigidbody 组件。

在检视器中,首先将方块的 Transform 设为 (0, 0.5,4)。在 Rigidbody 组件的 Constraints栏,勾上 Freeze Position 旁边的 Y。

太好了——现在让敌人以一种吓人的方式移动吧。在 Scripts 目录下新建脚本 Enemy。这个步骤你应该很熟悉了,如果忘记了,请参考前面的描述过的步骤。

然后,在类中声明变量:

public float moveSpeed;
public int health;
public int damage;
public Transform targetTransform;

这些变量的作用并不难猜。moveSpeed 先前在相机中也用到过,这里是同样的作用。health 和 damage 用于决定敌人什么时候死,以及它们对玩家造成的伤害。targetTransform 引用了玩家的 transform。

对于 Player 来说,你需要一个类描述玩家的所有属性,这一切恰好是敌人想摧毁的。

在项目浏览器中,选中 Player 文件夹并新建脚本 Player,这个脚本用于对碰撞进行处理,并保存玩家的生命值。双击脚本,打开它。

添加一个公共变量用于保存玩家的生命值:

public int health = 3;

这里为 health 设置了一个默认值,但你还可以在检视器中修改这个值。

要处理碰撞,添加如下方法:

void collidedWithEnemy(Enemy enemy) {// Enemy attack codeif(health <= 0) {// Todo }
}void OnCollisionEnter (Collision col) {Enemy enemy = col.collider.gameObject.GetComponent<Enemy>();collidedWithEnemy(enemy);
}

OnCollisionEnter() 方法会在两个带有碰撞体的刚体发生碰撞时触发。Collision 参数包含了交点和碰撞速度等信息。在这里,你只对 Collision 中的 Enemy 组件感兴趣,因此调用 collidedWithEnemy() 并执行攻击逻辑——这个在后面添加。

回到 Enemy.cs,添加下列方法:

void FixedUpdate () {if(targetTransform != null) {this.transform.position = Vector3.MoveTowards(this.transform.position, targetTransform.transform.position, Time.deltaTime * moveSpeed);}
}public void TakeDamage(int damage) {health -= damage;if(health <= 0) {Destroy(this.gameObject);}
}public void Attack(Player player) {player.health -= this.damage;Destroy(this.gameObject);
}

FixedUpdate() 方法你应该很熟悉了,略有不同的地方是,你用 MoveToward() 替代了 Lerp() 方法。这是因为敌人始终以同样的速度进行移动,当它到达目标后不需要减速。当敌人被子弹击中,TakeDamage() 方法被调用;当敌人的生命值变为 0,它将被销毁。Attack() 是类似的——它将伤害施加到 Player,然后敌人自动销毁。

回到 Player.cs,在 collidedWithEnemy() 方法中,将注释“Enemy attack code”替换成:

enemy.Attack(this);

在这个过程中,玩家被减血,敌人自毁。

返回 Unity。将 Enemy 脚本绑定到 Enemy 对象,在检视器中,修改 Enemy 的属性:

  1. Move Speed: 5
  2. Health: 2
  3. Damage: 1
  4. Target Transform: Player

现在你应该自己尝试着修改这些值。自己动手,然后和下面的 Gif 动画进行比较:

参考答案

在这个游戏中,当敌人和玩家发生碰撞,就会构成一次攻击。用 Unity 的物理引擎来检测碰撞不过是小菜一碟。

最终,将 Player 脚本绑定到结构视图中的 Player 上。

运行游戏,注意查看控制台:

当敌人碰上玩家,它会进行攻击并扣减玩家的生命值为 2。但控制台会抛出一个 NullReferenceException 错误,指向了 Player 脚本的这一行:

哎呀——玩家不仅仅和敌人发生了碰撞,也和游戏中的其它对象发生了碰撞,比如竞技场。因为这个对象没有 Enemy 脚本,因此 GetComponent() 返回了 null。

打开 Player.cs。在 OnCollisionEnter() 方法中,用一个 if 语句将 collidedWithEnemy() 方法包裹起来:

Open Player.cs. In OnCollisionEnter(), wrap collidedWithEnemy() in an if statements:
if(enemy) {collidedWithEnemy(enemy);
}

不会为空了!

使用预制件

只能逃跑、躲避敌人的游戏完全是一边倒的游戏。是该武装我们的玩家进行战斗的时候了!

点击结构视图中的 Create 按钮,然后选择 3D Object/Capsule。命名为 Projectile,然后设置它的 transform 为:

Position: (0, 0, 0)
Rotation: (90, 0, 0)
Scale: (0.075, 0.246, 0.075)

当玩家开火时,会发射一颗 Projectile 对象。要实现这个,你需要创建一个预制件。和场景中你曾经创建的对象不同,预制件是根据游戏逻辑按需创建的。

在 Assets 下新建一个文件夹 Prefabs。将 Projectile 对象拖进这个文件夹。现在,你就拥有了一个预制件!

你的预制件需要写点脚本。在 Scripts 目录下新建脚本 Projectile,声明如下变量:

public float speed;
public int damage;Vector3 shootDirection;

和教程里面其它会动的对象一样,这里也用到了速度和伤害变量,因为这也是战斗逻辑的一部分。shootDiretion 向量决定了子弹射向的方向。

要使用这个向量,需要定义如下方法:

// 1
void FixedUpdate () {this.transform.Translate(shootDirection * speed, Space.World);
}// 2
public void FireProjectile(Ray shootRay) {this.shootDirection = shootRay.direction;this.transform.position = shootRay.origin;
}// 3
void OnCollisionEnter (Collision col) {Enemy enemy = col.collider.gameObject.GetComponent<Enemy>();if(enemy) {enemy.TakeDamage(damage);}Destroy(this.gameObject);
}

上述代码解释如下:

  1. 子弹的运动和游戏中的其他对象不同。它没有目标,也没有实时为它施加外力;而是在它的整个生命周期中它都以既定的方向飞行。
  2. 这里,你会设置预制件的开始位置和方向。Ray 参数看起来比较神奇,待会你会学习它是如何计算的。
  3. 如果子弹碰到了敌人,它会调用 TakeDamage,然后自毁。

在场景结构视图中,将 Projectile 脚本绑定到 Projectile GameObject。将 Speed 设为 0.2,Damage 设为 1,然后点击检视器顶部附近的 Apply 按钮。这将修改应用到所有的预制件实例。

从场景结构视图中删除 Projectile 对象——你不再需要它了。

发射子弹

你已经有一个会飞会造成伤害的预制件了,接下来可以开始射击了。

在 Player 文件夹下,新建脚本 PlayerShooting,并将它绑定到场景的 Player 中。在脚本中声明变量如下:

public Projectile projectilePrefab;
public LayerMask mask;

第一个变量保存对之前创建的子弹预制件的引用。当玩家每次射击时,你都要创建一个该预制件的实例。掩码(mask 变量)用于过滤游戏对象。

等等,发射射线?这是什么魔法?

不,这里没有什么黑魔法——有时候你需要知道在某个方向上是否有碰撞发生。为了解决这个问题,Unity 从某个点开始向指定方向发射一条看不见的射线。很可能和这条射线相交的游戏对象会有很多,掩码(mask 变量)允许你过滤掉一些无关的对象。

Raycasts 超级好用,可以用于各种目的。通常会用它来判断其它角色在否中弹,也可以用它来判断在鼠标指针下面是否有某个几何体。要了解更多 Raycast 的用法,请阅读 Unity 官网上的 Unity 在线教学视频。

下图显示了一条从立方体到达圆锥体的射线。因为这条射线有一个 iconsphere 的掩码,它会忽略这个 GameObject 然后告诉你它击中了圆锥体:

让我们来发射自己的射线。

在 PlayerShooting.cs 中添加如下方法:

void shoot(RaycastHit hit){// 1var projectile = Instantiate(projectilePrefab).GetComponent<Projectile>();// 2var pointAboveFloor = hit.point + new Vector3(0, this.transform.position.y, 0);// 3var direction = pointAboveFloor - transform.position;// 4var shootRay = new Ray(this.transform.position, direction);Debug.DrawRay(shootRay.origin, shootRay.direction * 100.1f, Color.green, 2);// 5Physics.IgnoreCollision(GetComponent<Collider>(), projectile.GetComponent<Collider>());// 6projectile.FireProjectile(shootRay);
}

以上代码解释如下:

  1. 实例化一个子弹预制件,然后获取它的 Projectile 组件,以便初始化它。
  2. 这个点的坐标应该是 (x,0.5,z)。X 和 Z 是鼠标点击位置投射到地板上的坐标。这个转换非常重要,因为子弹必须平行于地板飞行——不然的话如果你可能会向下射击,只有菜鸟才会将子弹打到地上。
  3. 计算从玩家角色到 pointAboveFloor 的方向。
  4. 创建一条射线,通过起点和方向来描述它的轨迹。
  5. 这句告诉 Unity 物理引擎忽略玩家和子弹碰撞体之间发生的碰撞。否则当二者发生碰撞后, Projectile 脚本中的 OnCollisionEnter() 方法会被调用。
  6. 最后,设置子弹的飞行路线。

注意:在发射射线的时候调用 Debug.DrawRay() 非常有用,它会让你看见射线,以及它击中些什么。

射击逻辑写完,添加下列方法,让玩家可以真的开枪:


// 1
void raycastOnMouseClick () {RaycastHit hit;Ray rayToFloor = Camera.main.ScreenPointToRay(Input.mousePosition);Debug.DrawRay(rayToFloor.origin, rayToFloor.direction * 100.1f, Color.red, 2);if(Physics.Raycast(rayToFloor, out hit, 100.0f, mask, QueryTriggerInteraction.Collide)) {shoot(hit);}
}// 2
void Update () {bool mouseButtonDown = Input.GetMouseButtonDown(0);if(mouseButtonDown) {raycastOnMouseClick();  }
}

上述方法依序解释如下:

  1. 这个方法从相机发射一条射线到鼠标点击位置。然后在指定 LayerMask 的条件下,检查有没有游戏对象和它相交。
  2. 在帧刷新时,这个方法检查鼠标左键是否点击。如果点击,调用 raycastOnMouseClick()。

回到 Unity,在检视器中设置以下属性:

  • Projectile Prefab: 引用 Prefabs folder 文件夹中的 Projectile
  • Mask: Floor

注意:Unity 有一个事先定义的图层(layer)列表,这些图层在创建掩码时会用到。点击 GameObject 的 Layer 下拉框,点击 Add Layer 就可以新建图层:

要为 GameObject 指定图层,请在 Layer 下拉框中进行选择:

关于图层的更多内容,请参考 Unity 的 Layers 文档。

运行项目,开火吧!子弹飞向所指的方向,但还是有点不对,不是吗?

如果子弹的方向和飞行的方向保持一致就更好了。要解决这个问题,打开 Projectile.cs,添加如下方法:

void rotateInShootDirection() {Vector3 newRotation = Vector3.RotateTowards(transform.forward, shootDirection, 0.01f, 0.0f);transform.rotation = Quaternion.LookRotation(newRotation);
}

注意 RotateTowards 方法和 MoveTowards 方法很像,但它把向量当成是方向而不是位置。同时,你也不需要随时都去修改方向,因此只需要一个接近于 0 的 step 就够了。在 Unity 中,Transform 的 rotation 是用四元数表示的,这不属于本书范畴。在本教程中,你只需要知道它在进行和 3D 旋转相关的计算中比起向量来说更有优势即可。
如果对四元数和及其优点感兴趣,可以阅读这篇:How I learned to Stop Worrying and Love Quaternions。

在 FireProjectile() 方法最后,调用 rotateInShootDirection()。 FireProjectile() 最终变成这个样子:

public void FireProjectile(Ray shootRay) {this.shootDirection = shootRay.direction;this.transform.position = shootRay.origin;rotateInShootDirection();
}

再次运行游戏,向各个方向开火,这次子弹会直直指向射击的方向:

在不需要的时候可以删除 Debug.DrawRay 语句。

制造更多的坏蛋

只有一个敌人一点挑战性都没有嘛!但现在,自从你学过了预制件之后,你想要多少就可以制造出多少!:]

为了让玩家不可预知,你可以让每个 Enemy 的生命、速度和位置随机。

创建一个空的游戏对象——用 GameObject\Create Empty。命名它为 EnemyProducer 并添加一个 Box Collider 组件给它。在检视器中,修改它的属性:

  1. Position: (0, 0, 0)
  2. Box Collider:
    • Is Trigger: true
    • Center: (0, 0.5, 0)
    • Size: (29, 1, 29)

你绑定的这个碰撞体在竞技场中定义了一个特殊的 3D 空间。要看见它,在结构视图中选中 Enemy Producer 游戏对象,然后在场景视图中你会看到:

绿色的外框代表了这个碰撞体。

接下来你将编写脚本,用这个空间中的随机位置 X 和 Z 生成一个 Enemy 预制件的实例。

新建脚本 EnemyProducer,并绑定到 EnemyProducer 游戏对象上。在类文件中,添加成员:

public bool shouldSpawn;
public Enemy[] enemyPrefabs;
public float[] moveSpeedRange;
public int[] healthRange;private Bounds spawnArea;
private GameObject player;

第一个变量用于开启/禁止自动孵化。这个脚本会从 enemyPrefabs 中挑选一个随机的敌人预制件来实例化。后两个数组用于指定速度和生命值的最大、最小值。spawnArea 是你在场景视图中看见的那个绿色方框。最后,你需要引用玩家角色,将它作为坏蛋们的目标。

在这个脚本中,定义方法:

public void SpawnEnemies(bool shouldSpawn) {if(shouldSpawn) {player = GameObject.FindGameObjectWithTag("Player");}this.shouldSpawn = shouldSpawn;
}void Start () {spawnArea = this.GetComponent<BoxCollider>().bounds;SpawnEnemies(shouldSpawn);InvokeRepeating("spawnEnemy", 0.5f, 1.0f);
}

SpawnEnemies() 方法获取 Tag 标记为 Player 的游戏对象,并判断是否应当孵化出一个敌人。

Start() 方法初始化孵化区并在游戏启动后以 0.5 秒调用一个方法。这个方法每秒都会重复执行。除了充当 setter 方法,SpawnEnemies() 方法还会获取一个 Tag 为 Player 的游戏对象的引用。

但是,Player 游戏对象还没有设置 Tag 属性——你现在来做这个。从结构视图中选择 Player 对象,然后在检视器中,从 Tag 下拉菜单中选择 Player:

现在,需要编写孵化一个敌人的代码了。

打开 Enemy 脚本,添加方法:

public void Initialize(Transform target, float moveSpeed, int health) {this.targetTransform = target;this.moveSpeed = moveSpeed;this.health = health;
}

这个方法可以看成是一个 setter 方法,用于创建对象。接下来,这个代码会用于孵化出敌人。打开 EnemyProducer.cs,添加下列方法:

Vector3 randomSpawnPosition() {float x = Random.Range(spawnArea.min.x, spawnArea.max.x);float z = Random.Range(spawnArea.min.z, spawnArea.max.z);float y = 0.5f;return new Vector3(x, y, z);
}void spawnEnemy() {if(shouldSpawn == false || player == null) {return;}int index = Random.Range(0, enemyPrefabs.Length);var newEnemy = Instantiate(enemyPrefabs[index], randomSpawnPosition(), Quaternion.identity) as Enemy;newEnemy.Initialize(player.transform, Random.Range(moveSpeedRange[0], moveSpeedRange[1]), Random.Range(healthRange[0], healthRange[1]));
}

spawnEnemy() 方法会选择一个随机的敌人预制件,用随机的位置实例化预制件并初始化 Enemy 脚本中的公共变量。

EnemyProducer.cs 准备得差不多了!

回到 Unity。从结构视图拖一个 Ememy 对象到 Prefabs 文件夹,这将创建一个 Enemy 预制件。从场景中删除 enemy 对象——你用不到它了。然后将 Enemy Producer 脚本的公共变量修改为:

  1. Should Spawn: True
  2. Enemy Prefabs:

    • Size: 1
    • Element 0: Reference the enemy prefab
  3. Move Speed Range:

    • Size: 2
    • Element 0: 3
    • Element 1: 8
  4. Health Range:

    • Size: 2
    • Element 0: 2
    • Element 1: 6

运行游戏查看效果——源源不断的坏蛋们出来了!

好的,这些方块没一点也不吓人。是时候来加点料了。

在场景中创建一个 3D Cylinder 和 Capsule。分别命名为 Enemy2 和 Enemy 3 。如同你在第一个敌人中所做的,添加刚体组件和 Enemy 脚本给它们。选中 Enemy2 在检视器中修改它的属性:

  1. Scale: (0, 0.5, 0)
  2. Rigidbody:

    • Use Gravity: False
    • Freeze Position: Y
    • Freeze Rotation: X, Y, Z
  3. Enemy Component:

    • Move Speed: 5
    • Health: 2
    • Damage: 1
    • Target Transform: None

然后在 Enemy3 上重复同样步骤,但 Scale 设置为 0.7:

然后,将它们转成预制件,这和之前在原来的 Enemy 上的做法是一样的,然后在 Enemy Producer 中引用它们。在检视器中查看是这个样子:

  • Enemy Prefabs:

    • Size: 3
    • Element 0: Enemy
    • Element 1: Enemy2
    • Element 2: Enemy3

运行游戏,你会看到孵化出不同的预制件来了。

很快,你就会发现自己是无敌的!虽然这很爽,但你还是要让战斗均衡一点。

实现游戏控制器

你现在可以开枪和移动,敌人也出现了,接下来应该实现基本的游戏控制器。它会在玩家“死亡”之后重新开始游戏。但首先,你必须创建一种机制,通知所有感兴趣的对象玩家的生命值已经为 0。

打开 Player 脚本,在类声明之前添加:

using System;

在类中,添加一个新的公有时间:

public event Action<Player> onPlayerDeath;

事件是 C# 的语言特性,允许你将对象的变化广播给其它监听者。关于事件的用法,请阅读 Unity 的 event 在线教学视频。

将 collidedWithEnemy() 实现为如下代码:

void collidedWithEnemy(Enemy enemy) {enemy.Attack(this);if(health <= 0) {if(onPlayerDeath != null) {onPlayerDeath(this);}}
}

事件提供了一种简单的在对象之间实现信号状态改变的方法。一个游戏控制器应该对上面声明的事件很敏感。在 Scripts 文件夹中,新建脚本 GameController。双击文件打开它,添加如下变量:

public EnemyProducer enemyProducer;
public GameObject playerPrefab;

在这个脚本中我们需要控制敌人的生成,因为玩家死亡后还继续制造敌人是没有必要的。另外,重启游戏意味着需要重新创建玩家…也就是玩家也需要转成预制件。

添加下列方法:

void Start () {var player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();player.onPlayerDeath += onPlayerDeath;
}void onPlayerDeath(Player player) {enemyProducer.SpawnEnemies(false);Destroy(player.gameObject);Invoke("restartGame", 3);
}

在 Start() 方法中,获取了一个对 Player 脚本的引用,并且订阅了之前新建的事件。当玩家生命值归 0,onPlayerDeath() 方法会被调用,于是停止孵化敌人,将玩家从场景中移除,然后延迟 3 秒调用 restartGame() 方法。

最后来实现重启游戏的代码:

void restartGame() {var enemies = GameObject.FindGameObjectsWithTag("Enemy");foreach (var enemy in enemies){Destroy(enemy);}var playerObject = Instantiate(playerPrefab, new Vector3(0, 0.5f, 0), Quaternion.identity) as GameObject;var cameraRig = Camera.main.GetComponent<CameraRig>();cameraRig.target = playerObject;enemyProducer.SpawnEnemies(true);playerObject.GetComponent<Player>().onPlayerDeath += onPlayerDeath;
}

这里你需要进行一些清理工作:销毁场景中的敌人,创建新的 Player 对象。将相机的 target 设置为新实例,恢复敌人的孵化,让 Game Controller 订阅玩家死亡事件。

回到 Unity,打开 Prefabs 文件夹,将所有 Enemy 预制件的 tag 设为 Enemy。然后,将 Player 游戏对象拖进 Prefabs 文件夹。新建空游戏对象,命名为 GameController,将刚刚创建的脚本绑定上去。在检视器中,将所有的引用都连接上。

这个工作你已经很熟悉了。请自行完成引用的连接并检查是否和下面的答案一致:

  • Game Controller:

    • Enemy Producer: 从结构视图中引用 Enemy Producer
    • Player Prefab: 从 Prefabs 文件夹引用 Player Prefab

运行游戏,看游戏控制器是否运行正常。

好了,你用脚本完成了你的第一个 Unity 游戏!恭喜你 :]

结束

完成后的项目从这里下载。

现在,你应该掌握了如何构建一个简单的动作游戏。游戏制作不是简单工作;要完成整个游戏,大量的工作和脚本绝对只是其中一部分。为了添加更多的亮点,你必须在游戏中添加动画和 UI。因此,我强烈建议你阅读我们的这些教程:

  • Unity UI 入门
  • Unity 动画入门

如果你喜欢学习设计、编码和美化你的 Unity 游戏,请阅读 Unity 游戏教程。

这本书会教你构建 Unity 游戏所需要的一切只是,无论你是初学者还是有经验的开发者。在这本书中,你将编写出 4 个优秀的游戏:

  • 一个 3D twin-stick 射击游戏
  • 一个经典的 2D 平台游戏
  • 一个 3D 塔防游戏(支持 VR)
  • 一个第一人称射击游戏

希望你喜欢本教程,并激发你开始制作你一直想制作的游戏。有问题和建议,请在下面留言。

查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. angularJS+requireJS实现controller及directive的按需加载

    最近因为项目的比较大&#xff0c;需要加载的js文件较多&#xff0c;为了提高首屏页面的加载速度&#xff0c;需要对js文件进行按需加载&#xff0c;然后网上参考了一些资料&#xff0c;自己也深入研究一番之后&#xff0c;实现了按需加载控制器js文件及指令js文件的效果&#…...

    2024/4/21 6:25:24
  2. 徐州割南京 开切开双眼皮效果

    ...

    2024/4/21 6:25:23
  3. angularjs loading拦截器.请求,跳转页面等loading效果

    个人总结 前言: 今天周末,闲来无事又看了看拦截器,发现不只是截获http请求,同样还可以制作很多好玩的东西,今天来写一个loading案例,请求http或者跳转渲染页面时,会有一定的等待时间,可能一两秒,也可能是十几秒,在等待的过程中,用户是不知道的,所以我们要给用户一个直观的等待效…...

    2024/4/21 6:25:21
  4. kendoMaskedTextBox常用录入格式

    时间格式(HH:mm)$("#time_fm, #time_to").kendoMaskedTextBox({mask: "!:#$",rules: {"!": /[0-2]/,"": /[0-9]/,"#": /[0-5]/,"$": /[0-9]/,}});日期格式(MM/dd/yyyy)$("#birth_date, #due_date").ken…...

    2024/4/21 6:25:21
  5. 后台管理UI皮肤的选择

    后台管理UI的选择 目录 一、EasyUI二、DWZ JUI三、HUI四、BUI五、Ace Admin六、Metronic七、H UI八、Admin LTE九、INSPINIA十、LigerUI十一、FineUI十二、其它UI十三、总结最近要做一个企业的OA系统&#xff0c;以前一直使用EasyUI&#xff0c;一切都好&#xff0c;但感觉有点…...

    2024/4/21 6:25:23
  6. 我的CSDN现在没有C币,没办法下载

    CSDN,我总是没有机会去下载,我喜欢网盘方式的共享,有时候真的是急死人啊急死人。我也发了不少博客...

    2024/4/21 6:25:18
  7. 高分子压埋线法做双眼皮费用

    ...

    2024/4/21 6:25:17
  8. Web后台管理框架收集

    Web 开发中几乎的平台都需要一个后台管理&#xff0c;但是从零开发一套后台控制面板并不容易&#xff0c;幸运的是有很多开源免费的后台控制面板可以给开发者使用&#xff0c;以下是我整理的一些UI框架模板&#xff0c;可以拿来稍加改造就能直接使用 &#xff0c;简单实用 1、s…...

    2024/4/21 6:25:16
  9. 推荐几个后台管理界面

    AdminLTE GitHub 地址&#xff1a;Github.com/almasaeed2010/AdminLTE 基于 Bootstrap 3.x 的免费的后台 UI 框架 material-dashboard GitHub 地址&#xff1a;Github.com/creativetimofficial/material-dashboard 基于 Bootstrap 4 和 Material 风格的控制面板 iview-admi…...

    2024/4/21 6:25:15
  10. 管理后台-前端-AngularJS

    ngbootstrap可以做出很漂亮的管理系统出来&#xff0c;https://wrapbootstrap.com/可以付费购买&#xff0c;下文会提供一个免费的&#xff0c;要讲解如何从0到1把ng前端结构搭出来是很漫长的教程&#xff0c;本文仅仅介绍一下这个免费后台模版的结构&#xff0c;然后重点讲解如…...

    2024/4/21 6:25:15
  11. 徐州四院割最好的割最好的做割双眼皮最好整形医院

    ...

    2024/4/21 6:25:13
  12. 后台管理系统架构成型

    这段时间&#xff0c;集中精力突破后台管理系统的框架&#xff0c;希望能够踩在前人的肩膀上前行。 前面0.0.1版本的程序解决了物联网采集通道以及用户侧设备的协议代码开发。之前用到的物联网依赖的设备信息以及通道、协议信息&#xff0c;是直接在数据库里面创建的表&#x…...

    2024/4/21 6:25:12
  13. 10个开源web后台管理系统(二)

    Web 开发中几乎的平台都需要一个后台管理&#xff0c;但是从零开发一套后台控制面板并不容易&#xff0c;幸运的是有很多开源免费的后台控制面板可以给开发者使用 10个开源WEB后台管理系统&#xff08;二&#xff09; 10个开源WEB后台管理系统&#xff08;一&#xff09; 6.…...

    2024/4/21 6:25:11
  14. 一个基于Angular4+Bootstrap4+Scss的后台管理系统界面

    一个基于Angular4Bootstrap4Scss的后台管理系统界面&#xff0c;正在不断的开发中&#xff0c;有兴趣的朋友可以到我的github下载&#xff0c;github地址&#xff1a;点击进入github 一直在不断的更新中&#xff0c;最新的状态以github为准&#xff01; 欢迎加入群进行技术讨…...

    2024/4/20 19:07:21
  15. angular 管理后台

    http://blog.csdn.net/iamnieo/article/details/50474399 转载于:https://www.cnblogs.com/xfdmb/p/6197095.html...

    2024/4/20 19:07:23
  16. 后台管理系统,前端框架

    1&#xff1a;vue-element-admin 推荐指数&#xff1a;star:55k Github 地址&#xff1a;https://github.com/PanJiaChen/vue-element-admin Demo体验&#xff1a;https://panjiachen.github.io/vue-element-admin/#/dashboard 一个基于 vue2.0 和 Eelement 的控制面板 UI 框…...

    2024/4/20 19:07:18
  17. 为什么割双眼皮眼睛会大

    ...

    2024/4/20 19:07:17
  18. Angulr后台管理平台通过Angular加载页面过程

    ...

    2024/4/21 6:25:10
  19. 基于java后台,angular1为框架的admin管理系统完整项目体验报告

    不谈技术细节&#xff0c;直说大块的。 整体看&#xff0c;独立开发一个admin&#xff0c; 需要能力1&#xff0c;搭建环境&#xff0c;目录结构。环境上我选择gulp nodemon做本地打包和服务器环境&#xff0c;目录结构根据mvc框架&#xff0c;每一个需求点或者是页面&#x…...

    2024/4/21 6:25:09
  20. 基于angular的企业级中后台前端系统的一些思考和实践

    1、前后端统一的优势 (环境&#xff08;node&#xff09;&#xff0c;语言(typescript)&#xff0c;框架核心思想与用法(angular,nest)&#xff0c;编辑器等等)&#xff0c;降低配合成本&#xff0c;提高开发效率&#xff0c;提高代码的复用性&#xff0c;对每个人和公司全面发…...

    2024/4/21 6:25:09

最新文章

  1. 网络变压器在网络分析仪上能通过测试,装上设备后网速达不到呢?

    Hqst华轩盛(石门盈盛)电子导读&#xff1a;今天和大家一起探讨网络变压器在网络分析仪上能通过测试&#xff0c;装上设备后网通设备网速达不到的可能原因及其处理方式 一、出现这种情况可能有以下原因&#xff1a; 1.1. 设备兼容性问题&#xff1a;设备其它元器件与 网络…...

    2024/4/23 20:54:08
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. Node.js------Express

    ◆ 能够使用 express.static( ) 快 速 托 管 静 态 资 源◆ 能够使用 express 路 由 精 简 项 目 结 构◆ 能够使用常见的 express 中间件◆ 能够使用 express 创建API接口◆ 能够在 express 中启用cors跨域资源共享 一.初识Express 1.Express 简介 官方给出的概念&#xff…...

    2024/4/20 14:36:50
  4. PHP中常见的CRUD函数

    PHP中常见的CRUD函数 创建&#xff08;Create&#xff09;&#xff1a;创建一个新的数据记录 function create($name, $age, $email) {$conn mysqli_connect("localhost", "username", "password", "myDB");$sql "INSERT INT…...

    2024/4/22 9:22:19
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/4/22 17:02:52
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/4/23 13:30:22
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/23 13:28:06
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/4/20 23:26:47
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/23 13:27:44
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/19 11:57:53
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/23 13:29:53
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/23 13:27:22
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/23 13:28:42
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/4/21 20:01:37
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/23 13:29:23
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/23 13:27:46
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/23 13:47:22
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/19 11:59:23
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/4/19 11:59:44
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/23 13:28:08
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/23 13:29:47
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/23 13:28:14
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/4/23 13:27:51
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/23 13:27:19
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57