Unity之简单射击游戏案例
PS该案例来自学习的课本和我自己的理解。文章创作,还请多多支持呀各位美女、帅哥们!!!
废话不多说先上游戏实机操作视频
射击游戏实机操作_Trim
游戏内容大致如下
(1)完全使用键盘控制,由W、A、S、D键控制角色的方向移动,J键控制射击。(这样做主要是为了简化游戏输入部分的逻辑。)
(2)玩家具有多种武器,如手枪、霞弹枪和自动步枪,每种武器可以按Q键切换。
(3)场景上除了玩家角色还有若干敌人。敌人会向玩家方向移动并射击玩家。
(4)玩家角色和敌人都有生命值,中弹后生命值减少,减为零时则死亡。
那么接下来就看看实现过程吧!文末有游戏半成品分享,不嫌弃就玩玩看哈。
目录
1.角色脚本
2.跟随摄像机
3.武器系统
武器
子弹
4.射击的实现
5.游戏全局管理器
场景中,平台平面设为20x20的Plane
1.角色脚本
我们要创建出游戏玩家和敌人,包含(生命、移速等)属性,并给予他们特定的移动机制如下
玩家自由移动,但活动限制在平台区域。
敌人始终朝向敌人移动,但速度相对较慢。
我们要创建出游戏玩家和敌人,包含(生命、移速等)属性,并给予他们特定的移动机制如下
玩家自由移动,但活动限制在平台区域。
敌人始终朝向敌人移动,但速度相对较慢。
玩家脚本(Player)代码如下
//移动速度 public float c_speed; //最大血量 public float m_maxHp ; //输入方向变量 Vector3 c_input; //判断是否死亡 bool LifeState=true; //当前血量 private float c_Hp; void Start() { //刚开始满血 c_Hp = m_maxHp; } void Update() { //将键盘的横向、纵向输入,保存在input中 c_input = ne Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical")); bool fireKeyDon = Input.GetKeyDon(KeyCode.J); bool fireKeyPressed = Input.GetKey(KeyCode.J); bool changeWeapon = Input.GetKeyDon(KeyCode.Q); //判断是否dead,还活着就可以动 Debug.Log("现在还剩血量" + c_Hp); void Move() { //先归一化输入变量,让输入更直接,避免斜向移动时速度超过最大值 c_input =c_input.normalized; transform.position += c_input c_speed Time.deltaTime; //令角色前方与移动方向一致 if (c_input.magnitude > 0.1f) { transform.forard = c_input; } //限制移动范围 Vector3 c_VectorTemp=transform.position ; const float m_border = 20f; if(c_VectorTemp.x < -m_border) { c_VectorTemp.x = -m_border; } if (c_VectorTemp.x > m_border) { c_VectorTemp.x = m_border; } if (c_VectorTemp.z < -m_border) { c_VectorTemp.z = -m_border; } if (c_VectorTemp.z > m_border) { c_VectorTemp.z = m_border; } transform.position = c_VectorTemp; } }
敌人脚本(Enemy)代码如下
public float speed = 6; public float maxHp=4; Vector3 input; Transform player; float hp; bool lifeState = true; void Start() { player = GameObject.Find("Player").transform; hp = maxHp; } void Update() { EnemyMove(); Debug.Log("敌人还剩血量" + hp); } //敌人的移动机制 void EnemyMove() { //敌人的移动是始终朝向玩家的,他想干掉我们 input = player.position - transform.position; input = input.normalized; transform.position += input speed Time.deltaTime; if (input.magnitude > 0.1f) { transform.forard = input; } }
2.跟随摄像机
将摄像机调整至合适位置后,利用三维向量的差值,以玩家为原点为摄像机变换位置
将摄像机调整至合适位置后,利用三维向量的差值,以玩家为原点为摄像机变换位置
赋予主摄像机如下脚本(FolloPlayer)代码
public Transform folloTaeget; Vector3 offset; void Start() { //设置偏移量 offset = transform.position - folloTaeget.position; } void LateUpdate() { transform.position = folloTaeget.position + offset; }
上面代码中,摄像机的移动放在LateUpdate事件中,避免了帧数问题引起的游戏视角界面卡顿。
3.武器系统
敲黑板了!!!!!! 这一块灰常的重要哦。武器系统是玩家和敌人公用的一块。
武器
0.手枪单射,有射击间隔 ,我感觉没啥用的一把枪。
1.霰弹枪单射且射出五发子弹,射程短的可怜!
2.自动步枪连射,用起来挺爽的!
子弹
创建两个预制体分为PlayerBullet和EnemyBullet
属性具有生命周期
敲黑板了!!!!!! 这一块灰常的重要哦。武器系统是玩家和敌人公用的一块。
武器
0.手枪单射,有射击间隔 ,我感觉没啥用的一把枪。
1.霰弹枪单射且射出五发子弹,射程短的可怜!
2.自动步枪连射,用起来挺爽的!
子弹
创建两个预制体分为PlayerBullet和EnemyBullet
属性具有生命周期
这里先实现武器系统组件脚本,代码如下
public class Weapon : MonoBehaviour { //子弹的预制体 public GameObject prefabBullet; //几种武器的CD长度 public float pistolFireCD = 0.2f; public float shotgunFireCD = 0.5f; public float rifleFireCD = 0.1f; //上次开火时间 float lastFireTime; //当前的武器 public int curGun // 0:手枪,1霰弹枪,2自动步枪 { get; private set; } //设置开火函数 //keyDon指代单射,keyPressed指代连射 public void Fire(bool keyDon,bool keyPressed) { sitch (curGun) { case 0: if (keyDon) { PistolFire(); } break; case 1: if (keyDon) { ShotgunFire(); } break; case 2: if(keyPressed) { RifleFire(); } break; } } //更换武器 public int Change() { if (curGun != 3) { curGun++; } else { curGun = 0; } return curGun; } //手枪射击专用函数 public void PistolFire() { if (lastFireTime + pistolFireCD > Time.time) return; lastFireTime = Time.time; GameObject bullet = Instantiate(prefabBullet, null); bullet.transform.position = transform.position + transform.forard 1f; bullet.transform.forard = transform.forard; } //霰弹射击专用函数 public void ShotgunFire() { if (lastFireTime + shotgunFireCD > Time.time) return; lastFireTime = Time.time; //创建5颗子弹,相互间间隔10度,分布于前方扇形区域 for(int i = -2; i <= 2; i++) { GameObject bullet = Instantiate(prefabBullet, null); Vector3 dir = Quaternion.Euler(0, i 10, 0) transform.forard; bullet.transform.position = transform.position + dir 1f; bullet.transform.forard = dir; //霰弹枪的子弹射击距离很短,修改子弹的生命周期 Bullet b = bullet.GetComponent(); b.lifeTime = 0.3f; } } //自动步枪射击专用函数 public void RifleFire() { if (lastFireTime + rifleFireCD > Time.time) return; lastFireTime = Time.time; GameObject bullet = Instantiate(prefabBullet, null); bullet.transform.position = transform.position + transform.forard 1.2f; bullet.transform.forard = transform.forard; } void Start() { } void Update() { } }
接下是为子弹创建脚本,代码如下
public class Bullet : MonoBehaviour { public float c_bulletSpeed = 10f; //设置子弹的生命周期 public float lifeTime = 2; //子弹生成的时间 float startTime; void Start() { startTime = Time.time; } void Update() { //子弹移动 transform.position+= c_bulletSpeed transform.forard Time.deltaTime; //自毁装置 if (startTime + lifeTime < Time.time) Destroy(gameObject); } //中弹事件 private void OnTriggerEnter(Collider other) { //同类子弹碰撞不销毁 if (CompareTag(other.tag)) return; Destroy(gameObject); } }
上面的“中弹事件”在后文的射击的实现会用到滴!
4.射击的实现
有了武器,那么敌人和玩家的脚本都可以进行更新了,运用武器系统实例对象实现攻击。,在相互射击的过程中,如果一方中弹,则扣一滴血。
己方子弹射中自己不造成伤害,子弹击中对方后消失,子弹相互碰撞也消失。
子弹碰撞逻辑
碰撞事件是相对的,子弹碰撞的逻辑可以写在 Bullet 脚本、Player脚本或 Enemy 脚本中。在制作时最好单独考虑碰撞问题,然后统一编写。要制作的效果如下。
1、玩家角色的子弹击中敌人角色,会让敌人角色掉血。
2、敌人角色的子弹击中玩家角色,会让玩家角色掉血。
3、玩家角色的子弹不会击中玩家角色,敌人角色的子弹也不会击中敌人角色。
4、玩家角色的子弹与敌人角色的子弹可以相互抵消,同类子弹不能抵消。这一设计可以根据读者的偏好添加或取消。
从以上分析可以看出,“敌人角色的子弹”与“玩家角色的子弹”是截然不同的,最好用某种机制区分出二者。课本作者采用的方法是把这两种子弹做成两个不同的预制体,一个命名为PlayerBullet,另一个命名为EnemyBullet。然后利用标签(Tag)做出区分,前者的Tag是PlayerBullet,后者的Tag是EnemyBullet。
有了武器,那么敌人和玩家的脚本都可以进行更新了,运用武器系统实例对象实现攻击。,在相互射击的过程中,如果一方中弹,则扣一滴血。
己方子弹射中自己不造成伤害,子弹击中对方后消失,子弹相互碰撞也消失。
子弹碰撞逻辑
碰撞事件是相对的,子弹碰撞的逻辑可以写在 Bullet 脚本、Player脚本或 Enemy 脚本中。在制作时最好单独考虑碰撞问题,然后统一编写。要制作的效果如下。
1、玩家角色的子弹击中敌人角色,会让敌人角色掉血。
2、敌人角色的子弹击中玩家角色,会让玩家角色掉血。
3、玩家角色的子弹不会击中玩家角色,敌人角色的子弹也不会击中敌人角色。
4、玩家角色的子弹与敌人角色的子弹可以相互抵消,同类子弹不能抵消。这一设计可以根据读者的偏好添加或取消。
从以上分析可以看出,“敌人角色的子弹”与“玩家角色的子弹”是截然不同的,最好用某种机制区分出二者。课本作者采用的方法是把这两种子弹做成两个不同的预制体,一个命名为PlayerBullet,另一个命名为EnemyBullet。然后利用标签(Tag)做出区分,前者的Tag是PlayerBullet,后者的Tag是EnemyBullet。
那么,请在之前的Player脚本中添加如下代码
Weapon eapon; void Start() { //给武器类对象变量引用脚本 eapon = GetComponent(); } void Update() { if (LifeState==true ) { Move(); eapon.Fire(fireKeyDon, fireKeyPressed); if (changeWeapon) { eapon.Change(); } } else { Time.timeScale = 0; } private void OnTriggerEnter(Collider other) { if (other.CompareTag("EnemyBullet")) { if (c_Hp <= 0) { return; } c_Hp-=1; if (c_Hp == 0) { LifeState = false; } } }
相应的,对敌人的脚本(Enemy)也添加如下代码
public float fireTime = 1f; float m_time ; Weapon eapon; void Start() { eapon = gameObject.GetComponent(); m_time = Time.time; } void Update() { EnemyMove(); endFire(); Debug.Log("敌人还剩血量" + hp); if (hp <= 0) { // Instantiate(prefabBoomEffect, transform.position, transform.rotation); Destroy(gameObject); Debug.Log("恭喜你赢了!重要的事情说三遍"); Debug.Log("恭喜你赢了!重要的事情说三遍"); Debug.Log("恭喜你赢了!重要的事情说三遍"); } } //控制攻击间隔 void endFire() { if (m_time + fireTime < Time.time) { Fire(); m_time = Time.time; } } //敌人的攻击机制 void Fire() { eapon.Fire(true, true); } private void OnTriggerEnter(Collider other) { if (other.CompareTag("PlayerBullet")) { if (hp <= 0) { return; } hp--; if (hp == 0) { lifeState = false; } } }
5.游戏全局管理器
我还不会这一部分,那咱们就一下吧!【doge】
游戏的脚本一共是涉及下面六个
涉及的必要游戏物体有Player、Enemy、Plane
游戏半成品下载地址https://pan.baidu./s/1FtJMS23XGMH4FB2TKnuXDA
提取码PZPZ
PS终于写完了,睡觉了,困死了!!!!!!!
空调维修
- 温岭冰箱全国统一服务热线-全国统一人工【7X2
- 荆州速热热水器维修(荆州热水器维修)
- 昆山热水器故障码5ER-昆山热水器故障码26
- 温岭洗衣机24小时服务电话—(7X24小时)登记报
- 统帅热水器售后维修服务电话—— (7X24小时)登
- 阳江中央空调统一电话热线-阳江空调官方售后电
- 乌鲁木齐阳春燃气灶厂家服务热线
- 珠海许昌集成灶售后服务电话-全国统一人工【
- 乌鲁木齐中央空调维修服务专线-乌鲁木齐中央空
- 新沂热水器故障电话码维修-新沂热水器常见故障
- 诸城壁挂炉24小时服务热线电话
- 靖江空调24小时服务电话-——售后维修中心电话
- 空调室外滴水管维修(空调室外排水管维修)
- 九江壁挂炉400全国服务电话-(7X24小时)登记报修
- 热水器故障码f.22怎么解决-热水器f0故障解决方法
- 营口热水器售后维修服务电话—— 全国统一人工