Unity 3D-learning 打飞碟游戏改进版ben

一、改进打飞碟游戏

  • 游戏内容要求:
    1. 按 adapter模式 设计图修改飞碟游戏
    2. 使它同时支持物理运动与运动学(变换)运动
  • adapter模式设计简要展示:

SceneController 类,作为 Adaptee,实现了最初的方法。

IUserInterface、IQueryStatus、IJudgeEvent作为接口,定义了客户端调用的方法,作为Target。

SceneControllerBC类继承了SceneController类,作为Adpter。


二、具体实现

下面开始介绍游戏制作过程。

相比此前的牧师与魔鬼游戏,飞碟游戏里需要考虑的预设对象很少,只需要制作一个飞碟对象以及一个子弹对象。为了简化外形,我们设定飞盘为圆柱体,子弹为球体,具体的尺寸设置如下。

在子弹打击到飞碟时,考虑到飞碟 的摧毁效果,我们可以添加一个粒子系统名为explosion


与此同时,我们还需要显示出游戏的轮数和当前得分,所以我们需要设置 UI的Text控件


除此之外,最好有一个游戏说明,于是我们设计一个按钮,当按钮被按下时,会显示游戏说明


这个按钮并不在Hierarchy中设计,而是在IUserInterface类中的OnGUI函数中完成实现。

物理学属性刚体,可以更好的模拟物体的运动。以下是为飞盘、子弹对象添加刚体属性的截图: 



工厂模式并画出UML图


并按照UML图实现代码。

SceneControllerBC类的实现:

SceneController

using UnityEngine;  
using System.Collections;  
using Mygame;  

namespace Mygame {  
	public interface IUserInterface {  
		void emitDisk();  
	}  

	public interface IQueryStatus {  
		bool isCounting();  
		bool isShooting();  
		int getRound();  
		int getScore();  
		int getEmitTime();  
	}  

	public interface IJudgeEvent {  
		void nextRound();  
		void setScore(int Score);  
	}  

	public class SceneController : System.Object, IQueryStatus, IUserInterface, IJudgeEvent {  
		private static SceneController instance;  
		private SceneControllerBC baseCode;  
		private GameModel gameModel;  
		private Judge judge;  

		private int round;  
		private int score;  

		public static SceneController getInstance() {  
			if (instance == null) {  
				instance = new SceneController();  
			}  
			return instance;  
		}  

		public void setGameModel(GameModel obj) { 
			gameModel = obj;
		}  

		internal GameModel getGameModel() { 
			return gameModel;
		}  

		public void setJudge(Judge obj) { 
			judge = obj;
		}  

		internal Judge getJudge() { 
			return judge;
		}  

		public void setSceneControllerBC(SceneControllerBC obj) { 
			baseCode = obj;
		}  

		internal SceneControllerBC getSceneControllerBC() {
			return baseCode;
		}  

		public void emitDisk() { 
			gameModel.prepareToEmitDisk();
		}  
 
		public bool isCounting() { 
			return gameModel.isCounting();
		}  

		public bool isShooting() { 
			return gameModel.isShooting();
		}  

		public int getRound() { 
			return round; 
		}  

		public int getScore() { 
			return score;
		}  

		public int getEmitTime() { 
			return (int)gameModel.timeToEmit + 1;
		}  
 
		public void setScore(int score_) { 
			score = score_;
		}  
		public void nextRound() { 
			score = 0;
			baseCode.loadRoundData(++round);
		}  
	}  

	public class SceneControllerBC : MonoBehaviour {  
		private Color color;  
		private Vector3 emitPos;  
		private Vector3 emitDir;  
		private float speed;  

		void Awake() {  
			SceneController.getInstance().setSceneControllerBC(this);  
		}  

		public void loadRoundData(int round) {  
			if (round == 1) {
				color = Color.green;  
				emitPos = new Vector3(-2.5f, 0.2f, -5f);  
				emitDir = new Vector3(24.5f, 40.0f, 67f);  
				speed = 3;  
				SceneController.getInstance().getGameModel().setting(1, color, emitPos, emitDir.normalized, speed, 1);  
			}
			else if (round==2) {
				color = Color.red;  
				emitPos = new Vector3(2.5f, 0.2f, -5f);  
				emitDir = new Vector3(-24.5f, 35.0f, 67f);  
				speed = 4;  
				SceneController.getInstance().getGameModel().setting(1, color, emitPos, emitDir.normalized, speed, 2);  
			}
		}  
	}  
}  

DiskFactoryBC类的实现

1.DiskFactoryBC类的目的是管理飞碟实例,使用单例模式,得到飞碟id,得到飞碟对象,释放飞碟对象,为了避免开销大的问题,没有用destory,而是设置为diskList[id].SetActive(false);

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

namespace Mygame {  
	public class DiskFactory : System.Object {  
		private static DiskFactory _instance;  
		private static List<GameObject> diskList;
		public GameObject Disk;           

		public static DiskFactory getInstance() {  
			if (_instance == null) {  
				_instance = new DiskFactory();  
				diskList = new List<GameObject>();  
			}  
			return _instance;  
		}  


		public int getDisk() {   
			for (int i = 0; i < diskList.Count; ++i) {  
				if (!diskList[i].activeInHierarchy) {  
					return i;  
				}  
			}  
			diskList.Add(GameObject.Instantiate(Disk) as GameObject);  
			return diskList.Count-1;  
		}  

		public GameObject getDiskObject(int id) {  
			if (id > -1 && id < diskList.Count) {  
				return diskList[id];  
			}  
			return null;  
		}  
 
		public void free(int id) {  
			if (id > -1 && id < diskList.Count) {  
				diskList[id].GetComponent<Rigidbody>().velocity = Vector3.zero;  
				diskList[id].transform.localScale = Disk.transform.localScale;  
				diskList[id].SetActive(false);  
			}  
		}  
	}  
}  

public class DiskFactoryBC : MonoBehaviour {  
	public GameObject disk;  

	void Awake () {   
		DiskFactory.getInstance().Disk = disk;  
	}  
} 

实现Judge类

1.控制得分系统,打中飞碟加分,错过飞碟扣分,当分数累积到一定的程度自动进入下一关

using UnityEngine;  
using System.Collections;  
using Mygame;  

public class Judge : MonoBehaviour {  
	public int oneDiskScore = 10;  
	public int oneDiskFail = 10;  
	public int disksToWin = 4;  

	private SceneController scene;  

	void Awake() {  
		scene = SceneController.getInstance();  
		scene.setJudge(this);  
	}  

	void Start() {  
		scene.nextRound();  
	}  

	public void scoreADisk() {  
		scene.setScore(scene.getScore() + oneDiskScore);  
		if (scene.getScore() == disksToWin*oneDiskScore) {  
			scene.nextRound();  
		}  
	}  
		
	public void failADisk() {  
		scene.setScore(scene.getScore() - oneDiskFail);  
	}  
}  

实现IUserInterface类

1.用户界面,在此实现得分板,当前关卡的显示以及设置一个RepeatButton可以显示游戏规则;处理用户的输入,用户输入入有两种:鼠标左键和空格。左键发射子弹,空格发射飞碟。

        子弹射击的思路:当用户点击鼠标时,从摄像机到鼠标创建一条射线,射线的方向即是子弹发射的方向,子弹采用刚体组件,因此发射子弹只需要给子弹施加一个力。子弹对象只有一个,下一次发射子弹时,必须改变子弹的位置。为了不让子弹继承上一次发射的速度,必须将子弹的速度归零重置。采用射线而来判断子弹是否击中飞碟。     

using UnityEngine;  
using UnityEngine.UI;  
using System.Collections;  
using Mygame;  

public class UserInterface : MonoBehaviour {  
	public Text mainText;   
	public Text scoreText; 
	public Text roundText; 
	string ruleText = "按下空格键以发射飞碟,点击鼠标左键发射子弹,每打中一个飞碟可得到10分,每错过一个飞碟扣十分,累积到一定的分数可进入下一关。";

	private int round;  

	public GameObject bullet;           
	public ParticleSystem explosion;   
	public float fireRate = .25f;       
	public float speed = 500f;   

	private float nextFireTime;      

	private IUserInterface userInt;  
	private IQueryStatus queryInt;    

	void Start() {  
		bullet = GameObject.Instantiate(bullet) as GameObject;  
		explosion = GameObject.Instantiate(explosion) as ParticleSystem;  
		userInt = SceneController.getInstance() as IUserInterface;  
		queryInt = SceneController.getInstance() as IQueryStatus;  
	}  

	void Update () {  
		
		if (queryInt.isCounting()) {  
			mainText.text = ((int)queryInt.getEmitTime()).ToString();  
		}  
		else {  
			if (Input.GetKeyDown("space")) {  
				userInt.emitDisk();    
			}  
			if (queryInt.isShooting()) {  
				mainText.text = "";    
			}  
			if (queryInt.isShooting() && Input.GetMouseButtonDown(0) && Time.time > nextFireTime ) {  
				nextFireTime = Time.time + fireRate;  

				Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 
				bullet.GetComponent<Rigidbody>().velocity = Vector3.zero;     
				bullet.transform.position= transform.position;           
				bullet.GetComponent<Rigidbody>().AddForce(ray.direction*speed, ForceMode.Impulse);  

				RaycastHit hit;  
				if (Physics.Raycast(ray, out hit) && hit.collider.gameObject.tag == "Disk") {  
					explosion.transform.position = hit.collider.gameObject.transform.position;  
					explosion.GetComponent<Renderer>().material.color = hit.collider.gameObject.GetComponent<Renderer>().material.color;  
					explosion.Play();  
					hit.collider.gameObject.SetActive(false);  
				}  
			}  
		}  
		roundText.text = "Round: " + queryInt.getRound().ToString();  
		scoreText.text = "Score: " + queryInt.getScore().ToString();
		if (round != queryInt.getRound()) {  
			Debug.Log (round);
			round = queryInt.getRound();  
			mainText.text = "Round " + round.ToString() + " !";  
			Debug.Log (round);
		}  
	} 

	void OnGUI() {
		if (GUI.RepeatButton (new Rect (10, 10, 100, 40), "Help")) {
			GUI.TextArea(new Rect(10, 60, 750, 100),ruleText);
		}
	}
}  

在完成了脚本的设计后,将五个脚本都挂载到摄像机上

并且将预制好的游戏对象分别拖入到相应的位置即可完成游戏的设计。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值