太空射击游戏

 [Unity练习项目]

创建时间:2020-03-19 00:35:29       最近更新时间:2020-03-19 22:08:11

游戏日志详情

本练习项目主要用来了解Unity的基本使用方法,包括创建游戏体、键盘、鼠标操作,基本的物理碰撞、UI显示和逻辑处理等知识。

 

1. 导入资源并创建背景

  • 可通过直接复制粘贴的方式导入,或者在Project窗口中单击鼠标右键,选择【Import New Asset】进行导入。还可以在文件管理系统中,直接将资源拖入Assets文件夹中。
  • 直接创建【Plane】的背景,可将材质球拖到【Inspector】中Materials下的Element 0,来作为背景的材质。(注意将材质球的Rendering Mode设为Cutout,这样才能显示出透明效果)
  • 在【Window】->【Rendering】->【Lighting Settings】中更改Skebox(天空盒)为纯色;然后在场景中心添加点光源,调节灯光强度到合适。

 

2. 创建主角和子弹

  • 将飞船模型拖入场景,创建主角的脚本,添加响应鼠标或键盘按下的逻辑处理。
  • 将子弹模型拖入场景,并创建脚本,按一定速度往飞船面向的方向移动,其中实现OnBecameInvisible函数(当可渲染的物体离开可视范围,此函数会被自动触发),该函数作用为在子弹超出屏幕时,进行销毁。
  • 将子弹作为Prefab(预制体),通过飞船脚本进行子弹的创建。(通过Instantiate函数进行子弹的实例化)

 

3. 创建敌人和敌人生成器

  • 将敌人模型拖入场景,并创建脚本,定义敌人蛇皮走位,不过整体方向得向着飞船移动,其中还可以定时创建子弹(通过Instantiate函数),需要注意的是,子弹要向着主角方向发射。(Quaternion.LookRotation(主角位置-自身位置))

通过协程的方式进行创建敌人,代码如下:

[AddComponentMenu("GameScripts/EnemySpawn")]

public class EnemySpawn: MonoBehavior {
  public Transform m_enemyPrefab;

  void Start() {
    StartCoroutine(SpawnEnemy()); // 执行协程函数
  }

  IEnumerator SpawnEnemy() {
    while () { // 循环创建敌人
      yield return new WaitForSeconds(Random.Range(5, 15)); // 随机等待5-15秒
      Instantiate(m_enemyPrefab, transform.position, Quaternion.identity); // 生成敌人实例
    }
  }
}

 

4. 物理碰撞

  • 给主角、敌人、子弹添加碰撞体:在菜单栏中选择【Component】->【Physics】->【Box Collider】,并选中【Is Trigger】复选框;
  • 给主角、敌人、子弹添加碰撞体:在菜单栏中选择【Component】->【Physics】->【Rigidbody】,取消选择【Use Gravity】复选框来避免受到重力影响,选中【Is Kinematic】使游戏体的运动不受物理模拟影响。

对于所设置的非预制体的游戏体,可在Inspector窗口的右下角单击【Apply】按钮,则原始游戏体的Prefab和使用该Prefab的游戏体会自动更新设置。

 

5. 声音与特效

  • 给主角(或敌人)添加Audio Source组件:【Component】->【Audio】->【Audio Source】;
  • 选择爆炸特效的Prefab,为其添加Audio Source组件,然后将爆炸音效文件指定给Audio Source的Audio Clip中。由于默认【Play On Awake】处于选中状态,故当爆炸特效被实例化后,会自动播放爆炸音效。
  • Audio Source中【Spatial Blend】的值默认为0,表示音效为2D音效,不会受到空间环境影响,若设为1,音效则变为3D音效。

通过以下代码进行播放:

public AudioClip m_shootClip; // 声音文件
public Transform m_explosionFX; // 爆炸特效

void Update() {
  // 执行其他逻辑

  // 发射子弹时,播放一次射击声音
  AudioSource m_audio = this.GetComponent<AudioSource>();
  m_audio.PlayOneShot(m_shootClip);

  // 主角(或敌人)死亡后,进行销毁(或隐藏),在销毁(或隐藏)之前,实例化爆炸特效(爆炸特效是一个Prefab)
  if (m_life <= 0) {
    Instantiate(m_explosionFX, this.transform.position, Quaternion,identity);
    Destroy(this.gameObject);
  }
}

 

6. 游戏UI及战斗管理

对于游戏UI,需要注意的是,将Canvas的渲染模式改成:摄像机空间。

创建GameManager脚本,其中有个静态实例Instance,在Start(或Awake)函数中指向自身,以便于在其他类的对象中引用GameManager实例。

其中,GameManager控制UI界面的显示逻辑,并循环播放背景音乐(将Audio Source的loop属性设为true),还通过SceneManager.LoadScene来读取关卡。

在读取下一关卡时,当前关卡的游戏体都会被销毁,若希望保留一些游戏变量的值,可将这类变量设置为static。

最后,创建空游戏,并为其指定GameManager脚本组件即可。

 

7. 关卡跳转

在菜单栏中选中【File】->【Build Settings】,添加关卡。

 

8. 用鼠标控制主角

  • 选择【GameObject】->【3D Object】->【Quad】,创建一个平面物体,设置其位置和尺寸来铺满整个屏幕。然后取消物理组件【Mesh Collider】中【Mesh Render】的选中,从而隐藏平面物体的显示。
  • 新建一个层,指定给平面物体的Layer,而后面的鼠标操作产生的射线,只与此层中的平面物体碰撞;

在主角脚本中新增两个属性,然后更新Start,并在Update中调用新增的新增MoveTo函数:

protected Vector3 m_targetPos; // 主角所要移动的目标位置
public LayerMask m_inputMask; // 鼠标射线碰撞层(将上面新增的层指定到该属性)

void Start() {
  // 其他代码

  // 初始化目标位置
  m_targetPos = this.transform.position;
}

void MoveTo() {
  if (Input.GetMouseButton(0)) {
    Vector3 ms = Input.mousePosition; // 获得鼠标屏幕位置
    Ray ray = Camera.main.ScreenPointToRay(ms); // 将屏幕位置转为射线
    RaycastHit hitinfo; // 用来记录射线碰撞信息
    bool iscast = Physics.Raycast(ray, out hitinfo, 1000, m_inputMask);
    if (iscast) {
      m_targetPos = hitinfo.point; // 如果射中目标,记录射线碰撞点
    }
    // 使用MoveTowards函数,获得朝目标移动的位置
    Vector3 pos = Vector3.MoveTowards(this.transform.position, m_targetPos, m_speed*Time.deltaTime);
    // 更新当前位置
    this.transform.position = pos;
  }
}

 

9. 发布游戏

  • 【Edit】->【Project Settings】->【Player】,设置游戏名称、公司名和图标;
  • 不同平台有不同的构建版本选项。其中,在PC平台上,【Resolution】用于设置游戏窗口大小,【Run In Background】可使游戏窗口失去焦点后在后台继续允许,设置【Display Resolution Dialog】为【Enabled】,游戏在每次启动时会显示出一个用于设置显示分辨率的窗口。
  • 默认启动Unity游戏会看到Unity商标,若想去掉,则取消选中【Show Splash Screen】。不过该功能只能在Unity的专业版中使用!
反馈
╱╲