日落西山红霞飞~战士打靶把营归呀巴扎嘿。今天我制作一个简单的打靶游戏(扔飞镖)
在制作之前首先要思考这个游戏需要什么对象,很简单,一只飞镖、一个靶。
这里我把飞镖设置成了刚体,什么是刚体?简而言之,就是给这个物体加上了物理引擎,有了刚体,这个对象可以发生正常的碰撞,可以有质量、有阻力、有重力。这些属性自然我们可以通过时时改变游戏对象的Position和Rotation来模拟。但是这样子的话我们要时刻计算这个对象每个点对应的状态,计算开销十分之大。然而刚体就帮我们完成了所有的一切。
值得注意的是,刚体中通过改变位置来改变状态和通过施加力来改变位置这两种方法不能共存,刚体中Is Kinematic属性可以勾选是否为运动学,在运动学状态下,对象不再受力的作用,反之亦然。
刚体往往要配合碰撞器来使用,基础的碰撞器有盒碰撞器、球碰撞器等等,他们有自己的中心位置、大小等属性。比如一个球碰撞器:
绿线范围就是这个碰撞器的感应边缘
碰撞器可以理解为一个看不见的防御网,一旦有物体进入这个网,都会根据物理引擎模拟出被撞飞的效果(当然谁撞飞既取决于质量,也取决于是否为静态碰撞器,详情请查询具体的碰撞器和刚体的知识)
下面说一下我设计的思路
靶我是通过五个球体通过修改Z值变成圆饼做成的
对象树和做出来的效果如图
靶是一个空对象。这里对靶不设置为刚体,因为我把它作为了静态碰撞器。由于射中不同的环分数不一样,因此每个子对象都添加了一个碰撞器,这样子就能根据飞镖到底是撞到了哪个子对象从而可以计算相应的分数。
理想很丰满,现实很骨感,想用网格碰撞器,但是面太多不能用,用球碰撞器又不能把检测范围做成圆饼
这种范围就像结界一样,飞镖无论多快都无法穿过,隔空就被撞飞了。
那该怎么办呢?后来我有了新思路,就保持这种球体碰撞器,不同的是把它们都设为触发器,飞镖每穿过一个触发器就把这个触发器发送给靶,然后当飞镖头部刚好和靶重合的时候,把飞镖的刚体设为运动学,这样一来可以使得飞镖无法受到里的作用而产生飞镖卡在了靶上的效果,同时靶对象保存的最后一个触发器就是飞镖扎中的那个圆环。
那么思路明确了我们就可以开始做了。首先要有靶和飞镖吧,靶在上面已经做好了,那么展示一下我的飞镖(感觉像一个导弹)
代码也很简单,搞了三个类
1.Arrow类
这个就是飞镖的类
具体有如下几个功能:
- 在未射出时跟随鼠标改变位置
- 当扔出后给它施加力
便于重新开始,设置了Init函数用于初始化,IsFly判断是否被扔出,time记录飞行时间,为了模拟真实仍飞镖的情景,0.5秒内我给了飞镖一个向上的力,之后飞镖就只有重力和向前的力
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Arrow : MonoBehaviour {private int IsFly;private float time;private void Start(){Init();}public void Init(){IsFly = 0;transform.position = new Vector3(0, 0, -7);gameObject.GetComponent<Rigidbody>().isKinematic = false;gameObject.GetComponent<Rigidbody>().useGravity = false;}// Update is called once per framevoid Update () {if (IsFly == 0){Vector3 screenPos = Camera.main.WorldToScreenPoint(transform.position); // 目的获取z,在Start方法 Vector3 mousePos = Input.mousePosition;mousePos.z = screenPos.z; // 这个很关键 Vector3 worldPos = Camera.main.ScreenToWorldPoint(mousePos);this.transform.position = worldPos;if (Input.GetButtonDown("Fire1")){IsFly = 1;time = 0;}} else{time += Time.deltaTime;}}private void FixedUpdate(){if (IsFly == 1){Rigidbody rigid = this.gameObject.GetComponent<Rigidbody>();if (rigid){if (time < 0.5){rigid.AddForce(new Vector3(0, 5, 10));}else{rigid.AddForce(new Vector3(0, 0, 15));rigid.useGravity = true;}}}}
}
2.Target类
这个挂载在靶这个父对象身上,Catch判断飞镖是否被触发器捕捉到(没被捕捉到表示没有扎在靶上),Score记录得分,trigger记录最后一个被触发的触发器,arrow则是飞镖对象。
由于飞镖移动较快,检测可能没那么迅速,所以用了连续检测。由于我把靶放在了(0,0,0)位置上,所以只要当飞镖的位置的Z>0.05(飞镖头进入了靶)就可以把飞镖设为运动学。
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Target : MonoBehaviour {public int Catch;public int Score;private static Trigger trigger;public static Arrow arrow;// Use this for initializationprivate void Start(){print("hh");Init(); }public void Init(){if (arrow == null)arrow = Instantiate<Arrow>(Resources.Load<Arrow>("Prefabs/飞镖"));Catch = 0;Score = -1;trigger = null;arrow.Init();}public void Catched(){Catch = 1;}public static void SetTrigger(Trigger tri){trigger = tri;}private void OnGUI(){if (Score != -1){GUI.Label(new Rect(400, 80, 100, 30), "Your Score is " + Score);GUI.Label(new Rect(400, 100, 150, 30), "push blank to rebegin");}}// Update is called once per framevoid Update () {if (Input.GetAxis("Jump") > 0)Init();if (Catch == 1){if (arrow.transform.position.z > 0.05){arrow.GetComponent<Rigidbody>().isKinematic = true;arrow.GetComponent<Rigidbody>().useGravity = false;print(trigger.gameObject.name);switch(trigger.gameObject.name){case "圈1":Score = 5;break;case "圈2":Score = 4;break;case "圈3":Score = 3;break;case "圈4":Score = 2;break;case "圈5":Score = 1;break;}}return;}if (arrow.transform.position.z > 0.5)Score = 0;}
}
3.Trigger类
首先我们要把每个碰撞器的 Is Trigger设为true。它关联到靶对象(Target类),当触发器检测到飞镖,就把触发器自身发送给Target
代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Trigger : MonoBehaviour
{public Target target;// Use this for initialization// Update is called once per framevoid OnTriggerEnter(Collider collider){print("name : " + gameObject.name);target.Catched();Target.SetTrigger(this);}
}
好,简陋版就做出来了
拓展部份
可能这样子这个游戏很没有挑战性,那么我们还可以制造风,东西南北风!简单来说就是给飞镖添加别的力—— 一切阻碍它正常移动的力,也可以说,阻力。这个也很简单,就是时时给飞镖添加一个向左、向右、向后的阻力,一切都可以靠自己DIY