任务:子弹射击实例
1. 定义物理射线;
2. 实现从摄像机到鼠标目标位置射线发射子弹并碰撞目标对象的碰撞效果;
3. 功能:
- 利用子弹预制体生成子弹;
- 利用 cube 生成一墙面;墙面 cube 对象带有刚体和碰撞体组件;
- 利用 Random.range(float,float) 在随机的位置生成一个 cube 对象;
- 对随机生成的 cube 对象添加 box collider 碰撞体组件;
- 鼠标点击发射一带有刚体和碰撞体的子弹;
- 子弹撞击 cube 墙面后带有物理碰撞效果;
- 子弹跟随机生成的 cube 对象触发检测,触碰后销毁;
使用技巧
vs不错!
添加刚体组件
才会具有动力学特征,才会动
重力
两个组件:刚体,碰撞体
添加了碰撞体,才会发生碰撞
四种情况
Is Kinematic / 作为触发器:
可以穿透,不会碰撞
添加物理材质
创建一个主要的cube
创建一个RigidbodyMovement.cs脚本,并将其绑定到此cube上
主要功能:键盘操控,鼠标拖动,碰撞检测
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RigidbodyMovement : MonoBehaviour { Rigidbody rbody;//公开到显示面板 public public float speed = 30f;//速度预先定义在外部 // Start is called before the first frame update void Start() { rbody = GetComponent<Rigidbody>();//获取一个对象上面的刚体组件 } // Update is called once per frame void Update() { //获取虚拟轴,h、v定义在函数体内部 float h = Input.GetAxis("Horizontal");//获取水平轴,即左右键返回的数据 float v = Input.GetAxis("Vertical");//获取垂直轴,即上下键返回的数据 rbody.MovePosition(rbody.position + transform.right * h * Time.deltaTime * speed);//刚体左右移动 rbody.MovePosition(rbody.position + transform.forward * v * Time.deltaTime * speed);//刚体上下移动 } private void OnMouseDrag() { //通过鼠标扯拽控制刚体移动 transform.position += Vector3.right * speed * Time.deltaTime * Input.GetAxis("Mouse X"); transform.position += Vector3.up * speed * Time.deltaTime * Input.GetAxis("Mouse Y"); transform.position += Vector3.forward * speed * Time.deltaTime * Input.GetAxis("Mouse ScrollWheel"); } private void OnCollisionEnter(Collision collision) { //碰撞进入瞬间显示除地板外碰撞体名称 if (collision.gameObject.name != "Plane") { print("the name of collision enrty:" + collision.gameObject.name); } } private void OnCollisionStay(Collision collision) { //碰撞中显示除地板外碰撞体名称 if (collision.gameObject.name != "Plane") { print("the name of collision stay:" + collision.gameObject.name); } } private void OnCollisionExit(Collision collision) { //碰撞退出瞬间显示除地板外碰撞体名称 if (collision.gameObject.name != "Plane") { print("the name of collision exit:" + collision.gameObject.name); } } private void OnTriggerEnter(Collider other) { //如果另外一个碰撞体进入了触发器,则显示其名称 print("triggerEnter:" + other.gameObject.name); } private void OnTriggerStay(Collider other) { print("triggerStay:" + other.gameObject.name); } private void OnTriggerExit(Collider other) { print("triggerExit:" + other.gameObject.name); } }
MovePosition(三维向量) 改变位置
MoveRotation 让其旋转
效果如下
固定关节的用法
先创建一个空对象,再添加关节组件
关节要具有运动学特征才能被固定
所以要勾选 Is Kinematic
kinematic
adj. 运动的;运动学的; 这样红色方块无论是否受重力作用,都被固定在关节上了。
效果
HingeJoint 铰链关节
先创建一个空对象,再添加铰链关节组件
添加铰链关节组件会自动添加刚体组件
设定铰链关节的绑定对象:绿色方块 //这里不要绑错,不然没有反应
铰链关节属性设置
关闭重力作用(不影响cube的摆动),打开动力学属性(固定铰链,保持静止)
绿色方块属性设置
打开重力作用(摆动的动力来源),关闭动力学属性(这样不会静止) //为什么?不能理解。
绑定在空对象上的cube,这个transform属性的位置是相对位置。
先把相对位置设为(0,0,0),此时cube不会摆动。
改变z值,随便改一个,比如1,cube开始摆动。
效果
弹簧关节
先创建一个空对象,再添加弹簧关节组件
绑定球体至弹簧上
效果
点击以发射子弹
创建一个子弹(刚体)
把它做成预制体:
先创建一个Prefab文件夹,再把子弹拖进去,就变蓝了
创建BulletFire.cs脚本
using System.Collections; using System.Collections.Generic; using System.Drawing; using UnityEngine; public class BulletFire : MonoBehaviour { Ray ray;//定义射线 public Rigidbody bullet_rigidbody;//定义子弹刚体 RaycastHit raycastHit;//射线碰撞检测 // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if(Input.GetMouseButtonDown(0)) { ray = Camera.main.ScreenPointToRay(Input.mousePosition);//生成一条射线 if (Physics.Raycast(ray, out raycastHit))//射线检测:如果和其他物体碰撞,就发射子弹 { Rigidbody bullet = Instantiate<Rigidbody>(bullet_rigidbody);//定义子弹刚体,然后克隆一个子弹 //此处不用bullet.position,而是bullet.transform.position,两者有区别 bullet.transform.position = Camera.main.transform.position;//子弹初始位置设为摄像机的位置 bullet.AddForce((raycastHit.point - bullet.transform.position)*3f,ForceMode.Impulse);//AddForce函数:给刚体施加一个力,然后发射出去。注意括号。 //Destroy(raycastHit.collider.gameObject);//销毁点击对象 } } } }
把该脚本绑定到摄像机上,并在属性中指定子弹的预制体
效果(指哪打哪)
子弹的销毁(防止浪费内存资源)
创建一个DestroyBullet.cs脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DestoryBullet : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } private void OnCollisionEnter(Collision collision) { if(collision.gameObject.tag!="bullet") { return; }Destroy(collision.gameObject); } private void OnTriggerStay(Collider other) { if (other.gameObject.tag != "bullet") return; if (other.attachedRigidbody) other.attachedRigidbody.AddForce(Vector3.up * 20f); } }
通过碰撞检测+标签的方式实现
给子弹添加标签
如果与墙碰撞的东西是子弹,就销毁子弹。
把脚本绑定到墙上,记得把墙固定住
注意:墙不能设置为触发器,否则无法碰撞,也就无法销毁子弹
效果
子弹碰墙后被销毁
子弹穿过cube加速
(代码包含在上面)
cube设置为
透明材质(暂时不会)
效果
子弹明显加速
随机生成Cube(按A键控制生成),子弹若与其碰撞,将被销毁
创建cube预制体,设定为可被撞开(运动学on)
若用代码创建cube,则难以调整其运动学属性
GameObject cloneCube = Instantiate(cubeObj, new Vector3(0, 3, 0), Quaternion.identity); GameObject cubeObj = GameObject.CreatePrimitive(PrimitiveType.Cube); cubeObj.GetComponent<Renderer>().material.color = Color.cyan;
创建RandomCube.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class RandomCube : MonoBehaviour { public Rigidbody cubeObj; //public GameObject cubeObject; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { if (Input.GetKey(KeyCode.A)) { Rigidbody cloneCube=Instantiate<Rigidbody>(cubeObj); float x = Random.Range(-5.0f, 5.0f); float y = Random.Range(1f, 5.0f); float z = Random.Range(-5.0f, 5.0f); cubeObj.transform.position = new Vector3(x, y, z); cubeObj.tag = "RandomCube"; cubeObj.name = "RandomCube"; } } }
把RandomCube.cs挂到平面上
实现销毁子弹的效果,就把DestroyBullet.cs挂到RandomCube预制体上
效果
按A随机生成RandomCube,子弹撞击后,子弹销毁,RandomCube在撞击下运动
一张合照