物理系统与碰撞

Apr 23, 2018


改进飞碟(Hit UFO)游戏

更新内容

为飞碟添加刚体组件,运用物理引擎来实现飞行

添加PhysicActionManager和CCPhysicEmit来实现物理运动

根据适配器模式的概念,完全可以用SSActionManager做适配器,所以PhysicActionManager要继承SSActionManager

CCPhysicEmit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using UnityEngine;
using System.Collections;

public class CCPhysicEmit: SSAction
{
	public FirstController sceneController; 

	public static CCPhysicEmit GetSSAction()
	{  
		return ScriptableObject.CreateInstance<CCPhysicEmit>();
	}  
	// Use this for initialization  
	public override void Start()
	{
		sceneController = (FirstController)SSDirector.Instance.currentSceneController;
		int round = sceneController.round;
		if (round % 2 == 0) {
			round /= 2;
		}

		//添加Rigidbody组件,根据round设置force的大小
		var rig = gameobject.AddComponent(typeof(Rigidbody)) as Rigidbody;
		rig.velocity = Vector3.zero; //将速度置为0,力清空
		rig.AddForce (new Vector3 (Random.Range(-10f,10f),round,round*3), ForceMode.Impulse);
	}

	public override void Update() {

		//若此飞碟已经回收,则回调
		if (!gameobject.activeSelf) {
			this.destroy = true;
			this.callback.SSActionEvent (this);
		} else {

			//如果超出了边界-10,回收、回调
			if (this.transform.position.y < -10)  
			{  
				if (sceneController.round % 2 == 0) {
					if (sceneController.flag) {
						sceneController.onFlying = false;
						sceneController.flag = false;
					} else
						sceneController.flag = true;
				} else {
					sceneController.onFlying = false;

				}
				DiskFactory.instance.freeDisk (this.gameobject);
				this.destroy = true;  
				this.enable = false;  
				this.callback.SSActionEvent(this);  
			} 
		}
	}
}

打靶游戏

Image text

游戏内容

无限trials,但是风力会递增

最内层环5分,最外层环1分,脱靶没分

从鼠标点击位置射出箭,箭射到靶子上后颤动0.1秒

游戏架构

MVC架构,包含导演,场记。

门面模式, UserGUI类

动作分离,运用动作管理器,定义了emit和tremble动作

画面实现

靶子:五个Cylinder,越小的Cylinder越靠前

箭:一个Empty游戏对象,有四个子对象,一个Cylinder(箭杆),一个Sphere(箭尖),两个Cube(尾翼)

物理引擎的运用

为靶子添加碰撞核(Collider),但是不用触发(isTrigger = false)

为箭的Empty对象添加刚体(RigidBody)组件,并且将重力取消(gravity = false),默认为运动学(isKinematic = true),当箭在力的作用下飞行时,(isKinematic = false)

为箭的子对象箭头(Sphere)添加碰撞核,设置触发(isTrigger = true),添加脚本,监听碰撞的触发(void OnTriggerEnter(Collider c){}),碰撞时,要将Empty刚体组件的运动学取消(isKinematic = true)来停止箭的运动

ArrowCollider

挂载到箭头上(Empty的Sphere),监听碰撞事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using UnityEngine;
using System.Collections;

public class ArrowCollider : MonoBehaviour
{
	public ShootingController sceneController;
	// Use this for initialization
	void Start ()
	{
		sceneController = SSDirector.Instance.currentSceneController as ShootingController;
	}

	void OnTriggerEnter(Collider c) {  
		gameObject.transform.parent.transform.GetComponent<Rigidbody>().isKinematic = true;  
		gameObject.SetActive(false); //将箭头(Empty的Sphere)设置不在活动,在动作管理时可以用来判断是否已发生碰撞
		int score = 5 - int.Parse (c.gameObject.name); //根据环数记分,直接让场记记分
		sceneController.Count (score);
    }
}

SceneController

单纯地用来加载资源,动作由动作管理器来实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void LoadResources() {

		for (int i = 0; i < 5; i++) {
			var ring = GameObject.Instantiate<GameObject> (Resources.Load<GameObject> ("Prefabs/ring"));
			ring.name = i.ToString (); //根据环数起名字
			
			// 设置交替的颜色
			if (i % 2 == 0) {
				ring.GetComponent<Renderer> ().material.color = Color.red;
			} else {
				ring.GetComponent<Renderer> ().material.color = Color.white;
			}

			// 位置,大小。(小的环要更靠近相机,来成功实现碰撞触发)
			ring.transform.position += new Vector3 (0,0,i*0.01f);
			ring.transform.localScale = new Vector3 (i+1,1,i+1);
		}

		// 只维护一支箭,将箭头找出来,单独存储
		arrow = GameObject.Instantiate<GameObject> (Resources.Load<GameObject> ("Prefabs/arrow"));
		head = arrow.GetComponentsInChildren<Transform> () [4].gameObject;

	}

CCActionManager

检测到鼠标点击时,管理箭执行发射动作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 回调函数实现箭飞行结束,射到靶子上时颤动
public void SSActionEvent(SSAction source,
		SSActionEventType events = SSActionEventType.Competeted,
		int intParam = 0,
		string strParam = null,
		System.Object objectParam = null) {
		if(objectParam != null)
		{
			arrowTremble = CCArrowTremble.GetSSAction();
			this.RunAction(objectParam as GameObject, arrowTremble, this);
		}
	}
protected new void Update () {

		if (Input.GetMouseButtonDown(0))
			flag = true;
		else if (Input.GetMouseButtonUp(0) && flag) {
			sceneController.count++; // 场记维护了发射箭数目的变量
			arrowEmit = CCArrowEmit.GetSSAction();
			this.RunAction (sceneController.arrow, arrowEmit, this);
		}

		base.Update();  
	}

CCArrowEmit

发射动作的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
using UnityEngine;
using System.Collections;

public class CCArrowEmit: SSAction
{
	public ShootingController sceneController; 

	public static CCArrowEmit GetSSAction()
	{  
		return ScriptableObject.CreateInstance<CCArrowEmit>();
	}  
	// Use this for initialization  
	public override void Start()
	{
		sceneController = (ShootingController)SSDirector.Instance.currentSceneController;
		var d = Camera.main.ScreenPointToRay (Input.mousePosition).direction; //根据射线找到鼠标点击的方向
		sceneController.head.SetActive (true); // 激活箭头
		gameobject.SetActive (true); // 激活箭
		// 设置箭的位置和朝向(rotation)
		transform.position = new Vector3(d.x,d.y+1,0);
		transform.rotation = Quaternion.Euler(90,0,0);

		// 设置刚体的属性
		var rig = gameobject.GetComponent<Rigidbody>() as Rigidbody;
		rig.isKinematic = false; // 关闭运动学,使力发生作用
		rig.velocity = Vector3.zero; // 将原来的力及其速度清零
		rig.AddForce (d*30, ForceMode.Impulse); // 根据方向添加力,和风力
		rig.AddForce (sceneController.wind, ForceMode.Impulse);


	}

	public override void Update() {

		//根据箭头是否Active来判断是否以触发碰撞
		if (sceneController.head.activeSelf) {
			
			// 如果箭已经脱靶,结束飞行
			if (transform.position.z > 8) {
				transform.position = Vector3.up;
				sceneController.head.SetActive (false);
				gameobject.SetActive (false);
				this.callback.SSActionEvent (this); // 回调,参数objectParam为默认值null

			}
		} else {
			// 完成飞行,打开运动学
			var rig = gameobject.GetComponent<Rigidbody>() as Rigidbody;
			rig.isKinematic = true;
			this.destroy = true;
			this.callback.SSActionEvent (this,objectParam:gameobject);
		}

	}
}

CCArrowTremble

颤抖动作的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using UnityEngine;
using System.Collections;

public class CCArrowTremble: SSAction
{
	public ShootingController sceneController; 

	private float leftTime = 0.1f; // 颤抖时长

	private Vector3 trueLocation; // 原始位置

	public static CCArrowTremble GetSSAction()
	{  
		return ScriptableObject.CreateInstance<CCArrowTremble>();
	}  
	// Use this for initialization  
	public override void Start()
	{
		sceneController = (ShootingController)SSDirector.Instance.currentSceneController;
		trueLocation = transform.position;
	}

	public override void Update() {
		leftTime -= Time.deltaTime; // 计时
		if (leftTime < 0) {
			transform.position = trueLocation;
			this.destroy = true;
			this.callback.SSActionEvent (this);
		} else {
			transform.position += Vector3.left * Random.Range (-0.1f,0.1f); // 左右颤动
		}

	}
}

参考资料