本文只记录重要知识点以及涉及到的脚本 API,详细教学过程:视频链接 。
本文中大量关于 API 的描述来自 Unity 官方文档 。
另:学完之后发现这个教程非常非常像这个 Unity 官方教程 。
0. 搭建场景
这个简单的场景需要一个平面、四堵墙、一个球体作为玩家操控的对象,以及七个待收集的小方块。
搭完的场景如下所示:
1. 用方向键操纵球
首先需要为球添加刚体组件(Rigidbody),并为其挂载一个脚本,关键代码如下:
Rigidbody rigid;
float speed = 0.5f;
void Start()
{
rigid = GetComponent<Rigidbody>(); // 获取刚体组件
}
void Update()
{
var h = Input.GetAxis("Horizontal"); // 取值范围 -1 到 1,代表从左到右
var v = Input.GetAxis("Vertical"); // 取值范围 -1 到 1,代表从下到上
var movement = new Vector3(h, 0, v);
rigid.AddForce(movement * speed);
}
新学到的 API:
Input.GetAxis
public static float GetAxis(string axisName);
Returns the value of the virtual axis identified by axisName
.
The value will be in the range -1…1 for keyboard and joystick input.
那么有哪些轴可以调用呢?在 Unity 中点击 Edit -> Project Settings -> Input Manager 就能查看所有的轴以及键位,这段代码中就使用了Horizontal
和Vertical
,对应的键位是 wasd 或者方向键。
Rigidbody.AddForce
public void AddForce(Vector3 force, ForceMode mode = ForceMode.Force);
Adds a force to the Rigidbody.
Force is applied continuously along the direction of the force vector. Specifying the ForceMode
mode allows the type of force to be changed to an Acceleration, Impulse or Velocity Change.
以下是可以使用的ForceMode
,默认使用 Force:
成员 | 描述 |
---|---|
Force | Add a continuous force to the rigidbody, using its mass. |
Acceleration | Add a continuous acceleration to the rigidbody, ignoring its mass. |
Impulse | Add an instant force impulse to the rigidbody, using its mass. |
VelocityChange | Add an instant velocity change to the rigidbody, ignoring its mass. |
例如要使用 VelocityChange:
rigid.AddForce(movement * speed, ForceMode.VelocityChange);
2. 让小方块随时间匀速自转
为所有小方块挂载同样的脚本,关键代码如下:
void Update()
{
transform.Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
}
transform.Rotate
public void Rotate(Vector3 eulers, Space relativeTo = Space.Self);
参数 | 描述 |
---|---|
eulers | The rotation to apply. |
relativeTo | The rotation axes, which can be set to local axis (Space.Self ) or global axis (Space.World ) |
public void Rotate(float xAngle, float yAngle, float zAngle, Space relativeTo = Space.Self);
参数 | 描述 |
---|---|
relativeTo | Determines whether to rotate the GameObject either locally to the GameObject or relative to the Scene in world space. |
xAngle | Degrees to rotate the GameObject around the X axis. |
yAngle | Degrees to rotate the GameObject around the Y axis. |
zAngle | Degrees to rotate the GameObject around the Z axis. |
The implementation of this method applies a rotation of zAngle degrees around the z axis, xAngle degrees around the x axis, and yAngle degrees around the y axis (in that order).
Rotate can have the euler angle specified in 3 floats for x, y, and z.
Time.deltaTime
The completion time in seconds since the last frame (Read Only).
This property provides the time between the current and previous frame.
借助这个只读变量,就可以让小方块在每一秒的转动角度是相等的,而不是每一帧的转动角度相等。(通常每秒帧数随不同机器和时间而变化)
3. 当球碰撞到小方块时,让小方块消失
需要在球的脚本中加入下面这个函数:
private void OnTriggerEnter(Collider other)
{ // 这里用所有小方块都有的脚本组件来判断撞到的物体是否是小方块
// 个人觉得用标签来识别是更好的做法
// 总之用所有小方块共有的一个属性来识别即可
if (other.GetComponent<Pickup>())
{
other.gameObject.SetActive(false);
}
}
Collider.OnTriggerEnter
Parameters
other: The other Collider involved in this collision.
When a GameObject collides with another GameObject, Unity calls OnTriggerEnter.
OnTriggerEnter happens on the FixedUpdate function when two GameObjects collide. The Colliders involved are not always at the point of initial contact.
Note: Both GameObjects must contain a Collider component. One must have Collider.isTrigger enabled, and contain a Rigidbody. If both GameObjects have Collider.isTrigger enabled, no collision happens. The same applies when both GameObjects do not have a Rigidbody component.
根据上述官方文档的描述,我们的球和小方块都需要有 Collider 组件,且只能有其中一方激活 Collider.isTrigger。在这个例子中我们将所有小方块的 isTrigger 激活。
gameObject.SetActive
public void SetActive(bool value);
Activates/Deactivates the GameObject, depending on the given true or false value.
A GameObject may be inactive because a parent is not active. In that case, calling SetActive will not activate it, but only set the local state of the GameObject, which you can check using GameObject.activeSelf
. Unity can then use this state when all parents become active.
Deactivating a GameObject disables each component, including attached renderers, colliders, rigidbodies, and scripts.
4. 添加记分板和胜利条件
在小球脚本中的关键代码:
public UnityEngine.UI.Text tips;
int count = 0;
private void OnTriggerEnter(Collider other)
{
if (other.GetComponent<Pickup>())
{
other.gameObject.SetActive(false);
++count;
RefreshTips(); // 仅在碰撞时调用
}
}
void RefreshTips() // 自定义的函数,刷新记分板
{
tips.text = "Count: " + count.ToString();
if (count >= 7)
{
tips.text = "Win!";
}
}
需要在界面上新建一个 Canvas,在其中新建一个 Text,并将这个 Text 挂载到小球脚本的 tips 变量上面。
5. 脚本代码总览
5.1 小球脚本Player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public UnityEngine.UI.Text tips;
Rigidbody rigid;
float speed = 0.5f;
int count = 0;
// Start is called before the first frame update
void Start()
{
rigid = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
var h = Input.GetAxis("Horizontal"); // 取值范围 -1 到 1,代表从左到右
var v = Input.GetAxis("Vertical"); // 取值范围 -1 到 1,代表从下到上
var movement = new Vector3(h, 0, v);
rigid.AddForce(movement * speed, ForceMode.VelocityChange);
}
private void OnTriggerEnter(Collider other)
{
if (other.GetComponent<Pickup>())
{
other.gameObject.SetActive(false);
++count;
RefreshTips();
}
}
void RefreshTips()
{
tips.text = "Count: " + count.ToString();
if (count >= 7)
{
tips.text = "Win!";
}
}
}
5.2 方块脚本Pickup.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Pickup : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.Rotate(new Vector3(15, 30, 45) * Time.deltaTime);
}
}