Unity3DGame学习笔记(4):射箭游戏

打靶游戏要求:

1.靶对象为5环,按环计分;
2.箭对象,射中后要插在靶上;
3.游戏仅一轮,无限trials;
4.增强要求:添加一个风向个强度标志,提高难度;

游戏演示:


代码结构:


设计思路与细节:

1.构建基本游戏对象:

这里的主要游戏对象有两个,分别是靶子和箭。首先说一下靶子,如何通过unity的基本游戏对象创建一个靶子使得箭可以识别靶的不同环?在这里我通过将几个半径不同的扁圆柱体共圆心,且每一个的厚度由内向外递减(以比较小的差别),再配以不同颜色,就生成了一个近似现实世界的靶子。

下面是靶的各个部分的详细属性,其中bart为空对象,用来裝靶的各个部分,每一个部分有独立的碰撞器,这里我用Mesh Collider,这样方便我们后面识别箭打在靶上时打到了第几环。

然后是箭的设计,在这里我用几个基本物体组合起来,再调节一下大小和位置属性,其中脚本ArrowScript挂在箭上,用于识别触发并将信息传入ScoreRecorder进行计分。

2.射出箭矢:

在这里,我的思路是点击屏幕,箭矢能够向我所点击的方向射出。在这里,我用课上老师所讲的射线Ray来实现。在屏幕中(相机为透视模式),屏幕每一个点p对应标准面上的一个点p',射线就利用这个原理。我通过摄像机调用ScreenPointToRay函数,能得到从相机通过鼠标所点的屏幕点的光线 ,这样我们就有了一个理想的射击方向。代码实现如下(在UserInterface中):
		if (Input.GetMouseButtonDown(0)) {
			Ray mouseRay = camera.ScreenPointToRay (Input.mousePosition);
			Debug.Log("RayDir = " + mouseRay.direction);
			Controller.shootArrow (mouseRay.direction);
		}
顺便,我还实现了箭头也能面向点击方向,只需设置箭的up或forward等为射线方向即可(具体根据箭对象的头是up还是forward设置)。

3.触发识别(ArrowScript与ScoreRecorder):

在这里我用OnTriggerEnter来实现箭与靶碰撞的识别。根据上面介绍,我的靶是不同半径的圆柱体重叠而成,因为帧数与运动的原因,这样就有可能触发多个不同的环。那我就储存第一个碰到的环即可,只要曾经存过,就不再存储(我存环的名字来区别不同的环,为bartNum)。ArrowScript中代码如下:
	void OnTriggerEnter(Collider other) {;
		if (bartNum == "") {
			bartNum = other.gameObject.name;
			Debug.Log ("arrowTrigger = " + bartNum);


			Destroy (GetComponent<Rigidbody>()); //使得箭停在靶子上
			Component []comp = GetComponentsInChildren<CapsuleCollider>();
			foreach (CapsuleCollider i in comp) {
				i.enabled = false;
			}
			//使得箭不会触发靶子上的箭
			GetComponent<MeshCollider>().isTrigger = false;
			recorder.countScore (bartNum);
			//记录分数
		}
	}
ScoreRecorder中相关代码如下:
	public void countScore(string type) {
		if (type == "circle01")
			Score += 10;
		else if (type == "circle02")
			Score += 8;
		else if (type == "circle03")
			Score += 6;
		else if (type == "circle04")
			Score += 4;
		else if (type == "circle05")
			Score += 2;
		Debug.Log ("Score = " + Score);
	}

4.计时机制回收箭矢(ArrowScript和ArrowFactory):

因为要求箭矢要留在靶上,所以决不能在一碰撞就回收箭矢。为了实现工厂回收功能,我通过设置一个计时器来满足两个需求。ArrowScript中的代码如下:
        private float time; //计时器
	void Update () {
		time += Time.deltaTime;
		if (time >= LIMITTIME) {
			this.gameObject.SetActive (false);
			initial ();//重置变量值
		}
	}
ArrowFactory中相关代码如下:
	private void freeArrow() {
		for (int i = 0; i < UsedArrow.Count; i++) {
			GameObject temp = UsedArrow [i];
			if (!temp.activeInHierarchy) {
				UsedArrow.RemoveAt (i);
				FreeArrow.Add (temp);
			}
		}
	}

	void Update () {
		Debug.Log ("Used :" + UsedArrow.Count);
		Debug.Log ("Free :" + FreeArrow.Count);
		freeArrow ();
	}

完整代码:

1.UserInterface:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UserInterface : MonoBehaviour {
	private SceneController Controller;
	public Camera camera;
	public Text Score;
	public Text WindForce;
	public Text WindDirection;
	// Use this for initialization
	void Start () {
		Controller = (SceneController)FindObjectOfType(typeof(SceneController));
		Debug.Log ("Controller = "+ Controller);
	}
	
	// Update is called once per frame
	void Update () {
		Score.text = "Score : " + Controller.getRecorder ().getScore (); //显示分数
		float force = Controller.getActionManager ().getWindForce ();
		if (force < 0) {
			WindDirection.text = "Wind Direction : Left";
		} else if (force > 0) {
			WindDirection.text = "Wind Direction : Right";
		} else {
			WindDirection.text = "Wind Direction : No Wind";
		}
		//显示风向
		WindForce.text = "Wind Force : " + Controller.getActionManager ().getWindForce (); //显示风力
		if (Input.GetMouseButtonDown(0)) {
			Ray mouseRay = camera.ScreenPointToRay (Input.mousePosition);
			Debug.Log("RayDir = " + mouseRay.direction);
			Controller.shootArrow (mouseRay.direction);//以点击的方向发射箭矢
		}
	}
}
2.Director:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Director : System.Object {
	private static Director _instance;

	public SceneController Controller{ get; set;}

	public static Director getinstance() {
		if (_instance == null) {
			_instance = new Director ();
		}
		return _instance;
	}

	public int getFPS() {
		return Application.targetFrameRate;
	}

	public void setFPS(int fps) {
		Application.targetFrameRate = fps;
	}
}
3.SceneController:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SceneController : MonoBehaviour {
	private ActionManager actionManager;
	private ScoreRecorder scoreRecorder;
	private ArrowFactory arrowFactory;

	void Awake() {
		Director director = Director.getinstance ();
		director.setFPS (60);
		director.Controller = this;
		actionManager = (ActionManager)FindObjectOfType (typeof(ActionManager));
		scoreRecorder = (ScoreRecorder)FindObjectOfType (typeof(ScoreRecorder));
		arrowFactory = (ArrowFactory)FindObjectOfType (typeof(ArrowFactory));
		Debug.Log("Controller");
	}

	public ArrowFactory getFactory() {
		return arrowFactory;
	}

	public ScoreRecorder getRecorder() {
		return scoreRecorder;
	}

	public ActionManager getActionManager() {
		return actionManager;
	}

	public void shootArrow(Vector3 dir) {
		GameObject arrow = arrowFactory.getArrow (); //获得箭矢
		arrow.transform.position = new Vector3(0, 2, 0); //设置箭矢初始位置
		actionManager.shoot (arrow, dir);//调用ActionManager实现动作细节
	}
}
4.ArrowFactory:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ArrowFactory : MonoBehaviour {
	private List<GameObject> UsedArrow; //储存使用中的箭矢
	private List<GameObject> FreeArrow; //储存空闲箭矢
	private GameObject arrowPrefab;

	void Awake() {
		arrowPrefab = Instantiate(Resources.Load ("Prefabs/arrow")) as GameObject;
		arrowPrefab.SetActive (false);
		FreeArrow = new List<GameObject> ();
		UsedArrow = new List<GameObject> ();
		Debug.Log ("ArrowFactory");
	}
	// Update is called once per frame
	public GameObject getArrow() {
		GameObject temp;
		if (FreeArrow.Count == 0) {
			temp = GameObject.Instantiate (arrowPrefab) as GameObject;
			temp.SetActive (true);
			//如果空闲箭矢中没有箭矢,就生成新的箭矢
		} else {
			temp = FreeArrow [0];
			temp.SetActive (true);
			if (temp.GetComponent<Rigidbody>() == null)
				temp.AddComponent<Rigidbody> ();
			Component []comp = temp.GetComponentsInChildren<CapsuleCollider>();
			foreach (CapsuleCollider i in comp) {
				i.enabled = true;
			}
			temp.GetComponent<MeshCollider>().isTrigger = true;
			Debug.Log ("temp = " + temp);
			FreeArrow.RemoveAt (0);
			//空闲箭矢中有箭矢,初始化相关属性,加入使用
		}
		UsedArrow.Add (temp);
		return temp;
	}

	private void freeArrow() {
		for (int i = 0; i < UsedArrow.Count; i++) {
			GameObject temp = UsedArrow [i];
			if (!temp.activeInHierarchy) {
				UsedArrow.RemoveAt (i);
				FreeArrow.Add (temp);
			}
		}
	}

	void Update () {
		Debug.Log ("Used :" + UsedArrow.Count);
		Debug.Log ("Free :" + FreeArrow.Count);
		freeArrow ();
	}
}
5.ActionManager:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ActionManager : MonoBehaviour {
	private float speed = 40f; //箭矢初速
	private float Force = 0f; //第一箭风力默认为0
	void Awake() {
		Debug.Log ("ActionManager");
	}

	public void shoot(GameObject arrow, Vector3 dir) {
		arrow.transform.up = dir;
		//设置一下箭的朝向
		arrow.GetComponent<Rigidbody> ().velocity = dir * speed; //设置箭矢的速度。
		wind (arrow);
		Force = Random.Range(-100,100); //获取随机的风力
	}

	private void wind(GameObject arrow) {
		Debug.Log ("AddForce");
		arrow.GetComponent<Rigidbody> ().AddForce (new Vector3 (Force, 0, 0), ForceMode.Force); //对箭矢施加恒定的风力
	}

	public float getWindForce() {
		return Force;
	}
}
6.ScoreRecorder:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScoreRecorder : MonoBehaviour {
	private int Score = 0;//初始分数为0

	public void countScore(string type) {
		if (type == "circle01")
			Score += 10;
		else if (type == "circle02")
			Score += 8;
		else if (type == "circle03")
			Score += 6;
		else if (type == "circle04")
			Score += 4;
		else if (type == "circle05")
			Score += 2;
		Debug.Log ("Score = " + Score);
		//不同环不同分
	}

	public int getScore() {
		return Score;
	}
}
7.ArrowScript:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ArrowScript : MonoBehaviour {
	private string bartNum;
	private ScoreRecorder recorder;
	private float time; //计时器
	private static float LIMITTIME = 4;

	void initial() {
		time = 0;
		bartNum = "";
	}

	void OnTriggerEnter(Collider other) {;
		if (bartNum == "") {
			bartNum = other.gameObject.name;
			Debug.Log ("arrowTrigger = " + bartNum);

			Destroy (GetComponent<Rigidbody>()); //使得箭停在靶子上
			Component []comp = GetComponentsInChildren<CapsuleCollider>();
			foreach (CapsuleCollider i in comp) {
				i.enabled = false;
			}
			//使得箭不会触发靶子上的箭
			GetComponent<MeshCollider>().isTrigger = false;
			recorder.countScore (bartNum);
			//记录分数
		}
	}

	public string getBartNum() {
		return bartNum;
	}
	// Use this for initialization
	void Awake () {
		initial ();
		recorder = (ScoreRecorder)FindObjectOfType (typeof(ScoreRecorder));
	}

	// Update is called once per frame
	void Update () {
		time += Time.deltaTime;
		if (time >= LIMITTIME) {
			this.gameObject.SetActive (false);
			initial ();//重置变量值
		}
	}
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值