Unity3DGame学习笔记(1)

实验要求:

写一篇短文,描述以下游戏需求的实现。请写步骤,贴代码并解释:

·        实现点击效果。

o    Plane 或其他物体做地面, tag “Finish”

o   点击地面后,出现一个圆形攻击标记,两秒后自动消失。注意:该攻击标记不能挡住点击。(Primitive Objects / Cylinder

o   请使用一个简单工厂创建、管理这些的标记,并自动收回这些标记(注意,这些对象创建后,放在列表内,不必释放)。

用户仅需申请使用即可 GameObjectmyFactory.placeAttackMark(Vector3 postion)


这次要实现一个点击游戏,在一个平面上鼠标点击平面生成一个圆形攻击标志,所写代码结构如下图。




(1)      Sign标志类

因为要进行计时操作,所以我想如果每一个标志里有一个属性是记录开始时间,再与自定的世界时间进行比较,判断,即可实现计时功能。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Sign {
	public float StartTime;
	public GameObject sign;
}

所以在Sign类里除了有一个GameObject,还有一个开始时间StartTime。


(2)      实现对象管理

在这里,我声明了一个Factory类进行原形标志对象的管理,管理行为主要有三个,分别为初始化标志Start(),放置标志placeAttackMark()以及回收标志FreeSign()。首先是初始化标志,在Free队列里放进MaxSize个Sign对象,用于后面的放置。

	void Start () {
		Debug.Log ("Factory Start!");
		Used = new Queue<Sign>();
		Free = new Queue<Sign> ();
		for (int i = 0; i < MaxSize; i++) {
			Sign temp = new Sign ();
			GameObject sign = Instantiate (Resources.Load ("Prefabs/sign")) as GameObject;
			sign.SetActive (false);
			sign.layer = 1;
			temp.sign = sign;
			temp.StartTime = 0f;
			Debug.Log (temp.sign.name);
			Free.Enqueue (temp);
		}
	}

在这里我用了一个for循环,将我预制好的sign载入放进Free中,并设置一下各种变量数据,设置是否激化为否,即不显示。然后是放置标志,放置标志的操作也很简单,首先判断Free队列里是否有剩余,有则将队列首取出,设置开始的时间(用于计时操作),位置以及激活,最后将它放进Used队列中。最后是回收操作。

	public void placeAttackMark(Vector3 pos) {
		if (Free.Count != 0) {
			Sign temp = Free.Dequeue ();
			temp.StartTime = NowTime;
			temp.sign.transform.position = pos;
			temp.sign.SetActive (true);
			Used.Enqueue(temp);
		}
	}

因为队列有先进先出的特点,队列头元素一定是先进来的,所以对其进行时间判断,看它的StartTime与开始时间差是否大于等于2,是则出列,灭活并压进Free队列。

Free()需要在每一帧调用判断。NowTime是用于记录开始时间,在脚本一启动时就进行计算。

	public void FreeSign() {
		if (Used.Count == 0)
			return;
		if (NowTime - Used.Peek ().StartTime >= TimeLimit) {
			Sign temp = Used.Dequeue();
			temp.sign.SetActive (false);
			Free.Enqueue (temp);
		}
	}
	void Update () {
		NowTime += Time.deltaTime;
		FreeSign ();
	}


(3)      场景调用

现在我们已经有对象与对象管理器了,最后就是场景调用对象管理器的操作了。

首先是鼠标点击操作,这个很简单,直接调用函数Input.GetMouseButtonDown(0)即可。然后如何判断我们选取了一个物体呢,现在就需要Ray的操作了,利用Camera.ScreenPointToRy(Input.MousePosition)返回从相机通过屏幕点的光线。有了射线,我们还得取出射线经过的对象,这里我用RaycastRaycastHit(用于从raycast获取信息的结构)来实现。Raycast有多种变形,我用的是以下一种


	void Update () {

		if (Input.GetKeyDown (KeyCode.UpArrow)) {
			print ("UpArrow Down!");
		}

		if (Input.GetMouseButtonDown (0)) {
			Vector3 mp = Input.mousePosition; //get Screen Position

			//create ray, origin is camera, and direction to mousepoint
			Ray ray = ca.ScreenPointToRay (Input.mousePosition);

			//Return the ray's hit
			RaycastHit hit;
			if (Physics.Raycast(ray, out hit, Mathf.Infinity, 1)) {
				print (hit.transform.gameObject.name);
				if (hit.collider.gameObject.tag.Contains("Finish")) { //plane tag
					Fac.placeAttackMark(hit.point); //move to here
				}
			}
		}
	}

将我声明的RaycastHit放进去,通过out操作,就能取出对象放进我的RaycastHit中。还有一项工作就是忽略之前已经放置的标志,在这里我利用Raycast的LayerMasker参数实现。假如传进LayerMasker参数如1,函数就能忽略参数为1的物体,在初始化的时候,我就把所有标志的Layer设置为1,就能实现忽略操作。



(4)      计时器

我的计时原理非常简单,通过在一个Factory脚本里设置一个公共计时变量NowTime,

在每一个Sign里有一个会在放置时记录放置时间的StartTime,

通过对NowTime与StartTime的比较,大于等于限制时间的Sign就执行Free操作。

		if (NowTime - Used.Peek ().StartTime >= TimeLimit) {
			Sign temp = Used.Dequeue();
			temp.sign.SetActive (false);
			Free.Enqueue (temp);
		}



(5)      完整代码

1.SceneController.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SceneController : MonoBehaviour {
	Factory Fac;
	GameObject x;
	GameObject cam;
	Camera ca;

	// Use this for initialization
	void Start () {
		Debug.Log ("Controller Start!");
		cam = GameObject.Find ("Camera");
		x = GameObject.Find ("sign");
		ca = cam.GetComponent<Camera> ();
		Fac = (Factory)FindObjectOfType(typeof(Factory)) as Factory;
		Debug.Log (Fac);
	}

	// Update is called once per frame
	void Update () {

		if (Input.GetKeyDown (KeyCode.UpArrow)) {
			print ("UpArrow Down!");
		}

		if (Input.GetMouseButtonDown (0)) {
			Vector3 mp = Input.mousePosition; //get Screen Position

			//create ray, origin is camera, and direction to mousepoint
			Ray ray = ca.ScreenPointToRay (Input.mousePosition);

			//Return the ray's hit
			RaycastHit hit;
			if (Physics.Raycast(ray, out hit, Mathf.Infinity, 1)) {
				print (hit.transform.gameObject.name);
				if (hit.collider.gameObject.tag.Contains("Finish")) { //plane tag
					Fac.placeAttackMark(hit.point); //move to here
				}
			}
		}
	}
}

这里,必须将背景的plane的tag设为Finish,因为要识别背景物体是通过判断它tag是否为Finish来进行的。


2.Factory.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Factory : MonoBehaviour {
	private static int MaxSize = 3;
	Queue<Sign> Used;
	Queue<Sign> Free;
	float NowTime = 0f;
	static float TimeLimit = 2f;

	// Use this for initialization
	void Start () {
		Debug.Log ("Factory Start!");
		Used = new Queue<Sign>();
		Free = new Queue<Sign> ();
		for (int i = 0; i < MaxSize; i++) {
			Sign temp = new Sign ();
			GameObject sign = Instantiate (Resources.Load ("Prefabs/sign")) as GameObject;
			sign.SetActive (false);
			sign.layer = 1;
			temp.sign = sign;
			temp.StartTime = 0f;
			Debug.Log (temp.sign.name);
			Free.Enqueue (temp);
		}
	}
	
	// Update is called once per frame
	void Update () {
		NowTime += Time.deltaTime;
		FreeSign ();
	}

	public void placeAttackMark(Vector3 pos) {
		if (Free.Count != 0) {
			Sign temp = Free.Dequeue ();
			temp.StartTime = NowTime;
			temp.sign.transform.position = pos;
			temp.sign.SetActive (true);
			Used.Enqueue(temp);
		}
	}

	public void FreeSign() {
		if (Used.Count == 0)
			return;
		if (NowTime - Used.Peek ().StartTime >= TimeLimit) {
			Sign temp = Used.Dequeue();
			temp.sign.SetActive (false);
			Free.Enqueue (temp);
		}
	}
}

3Sign.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Sign {
	public float StartTime;
	public GameObject sign;
}









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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值