赞
踩
本案例是初级案例,意在引导想使用unity的初级开发者能较快的入门,体验unity开发的方便性和简易性能。
本次我们将使用团结引擎进行开发,帮助想体验团结引擎的入门开发者进行较快的环境熟悉。
本游戏是一个俯视角度的射击游戏。主角始终位于屏幕中心位置,玩家使用键盘控制主角移动,并可开枪射击场景中的敌人。玩家具有多种武器,如手枪、霰弹枪和自动步枪,玩家可以切换武器。敌人也会向玩家方向移动并射击玩家。当玩家角色或敌人的生命值减为零时则死亡。当玩家死亡后,游戏结束。
操作系统:Windows
Unity 版本:团结引擎1.0.0
团结引擎的安装参考《团结引擎的安装》
使用团结引擎新建项目和使用Unity Hub一样。打开Tuanjie Hub,点击左上角【create project】按钮,选择【3D】模版,输入项目名称和项目保存路径,点击右下【Create project】按钮完成创建。
1. 我们创建一个plane作为地面 。在Hierarchy面板上依次【右键】-> 【3D Object】->【plane】,将【Plane】创命名为【Ground】,并将x、z缩放大小变为4。我们将地面颜色改成灰色。新建文件夹,命名为【Materals】用来存放材质。在材质文件夹右键,依次选择【Create】->【Material】, 并命名为【Ground】。修改材质颜色为灰色(颜色值:4B4747),并将材质拖拽到地面上。
2. 同样, 我们创建一个胶囊体(Capsule)来作为主角,并命名为Player。创建一个立方体(Cube)放置在Player上,并调整到合适的位置。Cube将作为Player的正面,以便分辨方向。创建一个材质,命名为Face,改变材质样色为蓝色(也可以设置成自己喜欢的颜色),然后把这个材质赋值给Cube。
3. 调整摄像机角度
由于我们是俯视角度的射击游戏,因此需要调整摄像机的位置。选中摄像机,将摄像机调整到Player后方合适的位置。
4. 创建敌人
敌人角色与玩家角色造型基本一致,只是将白色换成红色。我们复制场景中的Player(选中Player, 按 ctrl + D),命名为Enermy。然后创建一个材质Enermy, 然后将材质的颜色改为红色。把材质赋给场景中的Enermy物体。新建标签(Tag),命名为Enermy,将玩家Player的Tag设置为“Player”, Enermy 的Tag设置为“Enermy”,方便区分玩家和敌人。之后我们新建文件夹,命名为 Prefabs,将Enermy制作成预知体(直接把Hierarchy中的Enermy物体拖到Prefabs文件夹中)。
上面基本场景已经搭建完毕,接下来我们就开始编写脚本,实现我们的游戏功能。
首先,我们来编写玩家脚本,让玩家动起来。新建文件夹,命名为【Scripts】,用来保存我自己编写的脚本文件。在【Scripts】右键->【Create】->【C# Script】,命名为Player。然后将Player脚本挂载到Player物体上。
和《3D小球跑酷》类似,我们使用键盘来控制Player的移动。双击打开Player脚本,输入以下内容:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Player : MonoBehaviour
- {
- public float speed = 10f; // 玩家的移动速度
- public int maxHp = 10; // 玩家的最大血量
-
-
- private int hp; // 玩家的当前血量
- private bool idDead = false;
-
-
-
- void Start()
- {
- hp = maxHp; // 初始血量为最大值
- }
-
- void Update()
- {
- if (!idDead)
- {
- Move();
- }
- }
-
- void Move()
- {
- var input = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); // 接受键盘输入
- input = input.normalized;
- Vector3 currentPos = transform.position;
- currentPos += input * speed * Time.deltaTime;
-
- if (input.magnitude > 0.1f)
- {
- transform.forward = input; // 使玩家面向移动方向
- }
-
- // 限制玩家的移动范围,范围为方圆20以内
- const float distance = 20f;
-
- if (currentPos.x > distance)
- currentPos.x = distance;
-
- if (currentPos.x < -distance)
- currentPos.x = -distance;
-
- if(currentPos.z > distance)
- currentPos.z = distance;
-
- if(currentPos.z < -distance)
- currentPos.z = -distance;
-
- transform.position = currentPos;
- }
- }
保存脚本,运行游戏,按键盘上的方向键或者A、D、S、W键,可以看到Player朝着指定的方向移动起来了。
注意:
以上移动方式没有考虑阻挡,为放置玩家或者敌人移动到地面的边缘掉下去,我们使用了简单的方式来限定他们的可移动范围。
运行游戏,发现Player在移动时,相机是固定的。下面我们把相机改为随着Player移动而移动。在《3D小球跑酷》我们采用的是把相机直接作为Player的子物体的方式,这里我们采用另一中方式(当然,你也可以使用作为子物体的方式),就是编写相机跟随脚本。
在Scripts文件夹下新建C#脚本,命名为FlowCamera。将脚本挂在到Camera上,双击打开脚本,输入以下代码:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class FlowCamera : MonoBehaviour
- {
- public Transform target;
-
- Vector3 offset;
-
-
- void Start()
- {
- offset = transform.position - target.position;
- }
-
-
- void Update()
- {
- transform.position = target.position + offset ;
- }
- }
将Player物体拖拽到脚本参数target字段。保存再次运行游戏,发现相机已经可以跟随Player移动了。
创建一个球体,大小缩放为0.2倍。命名为Bullet, 这将作为我们发射的子弹。新建脚本Bullet,将脚本挂在到Bullet物体上。双击打开脚本,输入以下内容:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Bullet : MonoBehaviour
- {
- public float lifeTime = 2f; // 子弹的生命周期
- public float speed = 10f; // 子弹的飞行速度
-
- float startTime; // 子弹的生成时间
-
-
- // Start is called before the first frame update
- void Start()
- {
- startTime = Time.time;
- }
-
- // Update is called once per frame
- void Update()
- {
- transform.position += speed * transform.forward * Time.deltaTime;
-
- // 超过一定时间后
- if (startTime + lifeTime < Time.time)
- {
- Destroy(gameObject); // 销毁子弹
- }
- }
-
-
- }
将球体拖到Prefabs文件夹,做成预制体。
下面我们实现一下Player切换武器的功能,这是我们游戏的一个重要功能。我们按Q键可以切换武器。武器有三种,分别是手枪(Pistol),自动步枪(Rifle)和散弹枪(shotgun)。
新建脚本,命名为Weapon。双击打开脚本,输入以下内容:
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Weapon : MonoBehaviour
- {
- public GameObject bulletPrefab; // 子弹预制体
-
- public float pistolFireCD = 0.2f; // 手枪发射子弹的时间间隔
- public float rifleFireCD = 0.1f; // 自动步枪发射子弹的时间间隔
- public float shotgunFireCD = 0.5f; // 散弹枪发射子弹的时间间隔
-
- float lastFireTime; // 上次开火时间
-
- public int curGun; //当前使用的武器 0:手枪 1:自动步枪 2:散弹枪
-
- // 开火方法,由角色脚本调用, keyDown 表示按一下就发射一颗子弹, keyPressed 表示长按
- public void Fire(bool keyDown, bool keyPressed)
- {
- switch (curGun)
- {
- case 0:
- if (keyDown)
- {
- PistolFire();
- }
- break;
- case 1:
- if (keyDown)
- {
- ShotgunFire();
- }
- break;
- case 2:
- if (keyPressed)
- {
- RifleFire();
- }
- break;
- default:
- break;
- }
- }
-
- // 更换武器
- public int ChangWeapon()
- {
- curGun += 1;
- if (curGun >= 3)
- curGun = 0;
-
- return curGun;
- }
-
- // 手枪射击
- private void PistolFire()
- {
- // 子弹发射时间间隔判定
- if (lastFireTime + pistolFireCD > Time.time)
- {
- return;
- }
-
- lastFireTime = Time.time;
-
- GameObject bullet = GameObject.Instantiate(bulletPrefab);
- bullet.transform.position = transform.position + transform.forward * 1.0f; // 子弹的位置,位于角色前方1米
- bullet.transform.forward = transform.forward; // 子弹的方向
-
- }
-
- // 自动步枪射击
- private void RifleFire()
- {
- // 子弹发射时间间隔判定
- if (lastFireTime + rifleFireCD > Time.time)
- {
- return;
- }
-
- lastFireTime = Time.time;
-
- GameObject bullet = GameObject.Instantiate(bulletPrefab);
- bullet.transform.position = transform.position + transform.forward * 1.0f; // 子弹的位置,位于角色前方1米
- bullet.transform.forward = transform.forward; // 子弹的方向
- }
-
- // 散弹枪射击,一次发射5颗子弹,子弹间隔10
- private void ShotgunFire()
- {
- // 子弹发射时间间隔判定
- if (lastFireTime + shotgunFireCD > Time.time)
- {
- return;
- }
-
- lastFireTime = Time.time;
-
- for (int i = -2; i <= 2; i++)
- {
- GameObject bullet = GameObject.Instantiate(bulletPrefab);
- var dir = Quaternion.Euler(0, i * 10, 0) * transform.forward;
-
- bullet.transform.position = transform.position + dir * 1.0f; // 子弹的位置,位于角色前方1米
- bullet.transform.forward = dir; // 子弹的方向
- }
- }
- }
修改Player脚本调用武器系统,使角色发射子弹。
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Player : MonoBehaviour
- {
- ......
-
- // 声明武器对象
- private Weapon weapon;
-
- void Start()
- {
- hp = maxHp; // 初始血量为最大值
- weapon = GetComponent<Weapon>(); // 获取武器对象
- }
-
- void Update()
- {
- if (!idDead)
- {
- Move();
- Fire(); // 开火,发射子弹
- ChangeWeapon(); // 切换武器
- }
- }
-
- void Move()
- {
- ......
- }
-
- void Fire()
- {
- bool keyDown = Input.GetKeyDown(KeyCode.J);
- bool keyPressed = Input.GetKey(KeyCode.J);
- weapon.Fire(keyDown, keyPressed);
- }
-
- void ChangeWeapon()
- {
- bool changWeapon = Input.GetKeyDown(KeyCode.Q);
- if (changWeapon)
- weapon.ChangWeapon();
- }
- }
将武器脚本(Weapon)挂载到Player物体上,同时把子弹预制体拖到武器脚本的bulletPrefab字段上。保存场景,运行游戏,按Q键可以切换枪支,按J键可以发射子弹。读者可以自行调整玩家的移动速度和子弹的发射速度,以获得更好的游戏体验。
敌人脚本和Player脚本类似,单敌人会始终朝着玩家前进,并朝着玩家发射子弹。在Scripts文件夹下新建脚本文件,命名为Enermy,并将其挂载到Enermy预制体上。双击打开脚本,输入以下内容:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Enermy : MonoBehaviour
- {
- private Transform playerTransform; // player 的 transform 组件
-
- public float speed = 2f; // 敌人的移动速度
- public int maxHp = 1; // 敌人的最大血量
-
-
- private int hp; // 敌人的当前血量
- private bool isDead = false;
-
- private Weapon weapon;
-
- void Start()
- {
- hp = maxHp; // 初始血量为最大值
- weapon = GetComponent<Weapon>();
- playerTransform = GameObject.Find("Player").transform;
- }
-
- void Update()
- {
- if (!isDead)
- {
- Move();
- Fire();
- }
- }
-
- void Move()
- {
- Vector3 dir = playerTransform.position - transform.position;
- var currentPos = transform.position + dir.normalized * speed * Time.deltaTime;
-
- // 限制敌人的移动范围,范围为方圆20以内
- const float distance = 20f;
-
- if (currentPos.x > distance)
- currentPos.x = distance;
-
- if (currentPos.x < -distance)
- currentPos.x = -distance;
-
- if (currentPos.z > distance)
- currentPos.z = distance;
-
- if (currentPos.z < -distance)
- currentPos.z = -distance;
-
- transform.position = currentPos;
- transform.forward = dir.normalized;
- }
-
- void Fire()
- {
- weapon.Fire(true, true);
- }
-
-
- }
然后,把武器脚本挂载到Enermy预制体上,保存。再场景中放置几个敌人(直接把Enermy预制体拖到场景中即可),运行游戏,发现敌人开始朝着玩家移动,并不断发射子弹。
虽然现在我们可以控制Player移动,并且能发射子弹,敌人也能够发射子弹,但是子弹碰到敌人或者Player都没有反应,接下来我们就开始添加子弹碰撞的逻辑。
子弹和物体的碰撞分为以下几种情况:
1) Player的子弹击中敌人
2)敌人的子弹击中Player
3)玩家的子弹和Player的子弹碰撞
4)Player的子弹不会击中Player,敌人的子弹也不会击中敌人。
为了区分敌人子弹和Player的子弹,我们采用标签(Tag)来进行区分。我们新建两个标签,分别为PlayerBullet和EnermyBullet。选中之前的子弹预制体,重命名为PlayerBullet, 并将Tag设置为PlayerBullet。复制PlayerBullet,重命名为EnermyBullet,并将Tag设置为EnermyBullet。
双击Enermy预制体,将脚本Weapon上的BulletPrefab字段设置为EnermyBullet。保存退出。
双击Bullet脚本,添加碰撞逻辑。添加以下代码:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Bullet : MonoBehaviour
- {
- .....
-
- private void OnTriggerEnter(Collider other)
- {
- if (CompareTag(other.tag)) // 如果子弹的tag和碰撞到的物体的Tag相同
- return;
-
- Destroy(gameObject);
- }
-
- }
当敌人的子弹碰到玩家的时候,玩家血量减1,当血量为0时,玩家死亡,游戏结束。双击打来Player脚本,添加以下代码:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Player : MonoBehaviour
- {
- ......
-
- private void OnTriggerEnter(Collider other)
- {
- if (other.CompareTag("EnermyBullet"))
- {
- if (hp <= 0) return;
- hp--;
- if (hp <= 0)
- {
- idDead = true;
- Debug.Log("Game Over!");
- }
- }
- }
- }
当玩家的子弹碰到敌人的时候,敌人血量减1。当敌人的血量为0时,敌人死亡。双击打开Enermy脚本,添加以下代码:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Enermy : MonoBehaviour
- {
- ......
-
- private void OnTriggerEnter(Collider other)
- {
- if (other.CompareTag("PlayerBullet"))
- {
- if (hp <= 0) return;
- hp--;
- if (hp <= 0)
- {
- Destroy(gameObject);
- }
- }
- }
- }
保存场景,运行游戏,发现子弹碰撞后并没有什么效果,这是因为我们没有添加刚体,并设置触发器。
选中敌人的子弹和Player的子弹的预制体,添加刚体组件(RigidBody),并勾选 Collider 上的Is Trigger 复选框。选择Enermy预制体,并勾选 Collider 上的Is Trigger 复选框。选择Player,并勾选 Collider 上的Is Trigger 复选框。保存场景再次运行游戏,发现Player 被击中之后,血量减少,当血量减少到0是,控制台输出“Game Over”。
现在我们的游戏已经初具模型了,单要手动把敌人预制体拖到场景中,如果要有许多敌人就显得太麻烦了,而且拖到场景中的敌人有限,杀完了就没有了。 我们要有一个可以源源不断生成敌人的机制,每隔一段时间就生成一个敌人,这样就可以一直玩下去了。
在场景中新建一个空物体,命名为EnemySpawn,同时新建脚本文件,也命名为EnemySpawn,并挂载到EnemySpawn物体上。脚本内容如下:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class EnermySpawn : MonoBehaviour
- {
- public GameObject enermyPrefab;
-
- public float spawTimer = 2.0f;
-
- List<GameObject> enermies = new List<GameObject> ();
-
- void Update()
- {
- if (enermies.Count >= 10) return; // 场景中最多10个敌人
- spawTimer -= Time.deltaTime;
- if (spawTimer <= 0)
- {
- spawTimer = 2.0f;
-
- GameObject enermy = Instantiate(enermyPrefab);
- enermy.transform.position = new Vector3(-20, 1, -20);
- }
- }
- }
保存脚本和场景,运行游戏,发现没隔2秒就会有一个敌人生成,并且场景中最多会有10个敌人,当场景中的敌人数量少于10个时,又会有新的敌人生成出来,这样就可以一直玩下去了。
好了,射击游戏到这里就结束了,虽然还有很多不足,但作为初学者的练习案例,也用到了许多Unity的基础知识,希望您能够获得一些启发,对您的学习有所帮助。您也可以发挥自己的想想力,完善本案例,比如增加积分、音效等等,使游戏更加丰富。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。