打飞碟

飞碟游戏
游戏内容:
1.游戏有 n 个 round,每个 round 都包括10 次 trial;
2.每个 trial 的飞碟的色彩,大小;发射位置,速度,角度,受该 round 的 ruler 控制;
3.每个 trial 的飞碟有随机性,总体难度随 round 上升;
4.鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
游戏涉及的知识点:
1.世界与屏幕坐标、射线与碰撞、动态修改shader
2.支持mono的单实例、游戏对象工厂、扩展游戏对象属性与行为的方法

通过空格发射飞碟,鼠标点击求使其爆炸,做了一个爆炸的效果。等会会在下面介绍的。总共有三关,第一关会1个/次,第二关2个/次,第三关3个/次。


1、 MainSceneController.cs:

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

namespace PlayDisks.Com {  

	public interface IUserAction {  
		void launchDisks();  
		void strikeTheDisk(Vector3 mousePos);  
	}  

	public interface IGameStatusOp {  
		int getRoundNum();  
		void addScore();  
		void subScore();  
	}  

	public class MainSceneController : System.Object, IUserAction, IGameStatusOp {  
		private static MainSceneController instance;  
		private GameModel myGameModel;  
		private GameStatus myGameStatus;  

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

		internal void setGameModel(GameModel _myGameModel) {  
			if (myGameModel == null) {  
				myGameModel = _myGameModel;  
			}  
		}  

		internal void setGameStatus(GameStatus _myGameStatus) {  
			if (myGameStatus == null) {  
				myGameStatus = _myGameStatus;  
			}  
		}  

		/** 
        * 实现IUserAction接口 
        */  
		public void launchDisks() {  
			myGameModel.launchDisks();  
		}  

		public void strikeTheDisk(Vector3 mousePos) {  
			myGameModel.strikeTheDisk(mousePos);  
		}  


		/** 
        * 实现IGameStatusOp接口 
        */  
		public int getRoundNum() {  
			return myGameStatus.getRoundNum();  
		}  

		public void addScore() {  
			myGameStatus.addScore();  
		}  

		public void subScore() {  
			myGameStatus.subScore();  
		}  
	}  
}  


2 、UserInterface.cs:
using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
using PlayDisks.Com;  

public class UserInterface : MonoBehaviour {  
	private IUserAction action;  

	void Start () {  
		action = MainSceneController.getInstance() as IUserAction;  

	}  

	void Update () {  
		detectSpaceKeyAndLaunchDisks();  
		detectMouseDownAndStrikeTheDisk();  
	}  

	void detectSpaceKeyAndLaunchDisks() {  
		if (Input.GetKeyDown(KeyCode.Space)) {  
			action.launchDisks();  
		}  
	}  

	void detectMouseDownAndStrikeTheDisk() {  
		if (Input.GetMouseButtonDown(0)) {  
			Vector3 mouseWorldPosition = Input.mousePosition;  
			action.strikeTheDisk(mouseWorldPosition);  
		}  
	}  
}  


在本节课上我们学到了要建立缓存工厂。
(5)带缓存工厂模式的实现
getDisk(ruler) 伪代码
IF (free list has disk) THEN
a_disk = remove one from list
ELSE
a_disk = clone from Prefabs
ENDIF
Set DiskData of a_disk with the ruler
Add a_disk to used list
Return a_disk
FreeDisk(disk)
Find disk in used list
IF (not found) THEN THROW exception
Move disk from used to free list
3、DartFactoryBC.cs:
飞碟工厂,实现getDisk生产飞碟,通过FreeDisks()回收。
建立工厂的好处:游戏对象的创建与销毁高成本,必须减少销毁次数。如:游戏中子弹
屏蔽创建与销毁的业务逻辑,使程序易于扩展


using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
using PlayDisks.Com;  

namespace PlayDisks.Com {  
	public class DiskFactory : System.Object {  
		private static DiskFactory instance;  
		private List<GameObject> usingDiskList = new List<GameObject>();   //正在使用的飞镖list  
		private List<GameObject> unusedDiskList = new List<GameObject>();  //没有使用的飞镖list  

		private GameObject DiskItem;  

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

		//提供飞镖  
		public GameObject getDisk() {  
			if (unusedDiskList.Count == 0) {    //没有存储飞镖  
				GameObject newDisk = Camera.Instantiate(DiskItem);  
				usingDiskList.Add(newDisk);  
				return newDisk;  
			}  
			else {                      //有存储飞镖  
				GameObject oldDisk = unusedDiskList[0];  
				unusedDiskList.RemoveAt(0);  
				oldDisk.SetActive(true);  
				usingDiskList.Add(oldDisk);  
				return oldDisk;  
			}  
		}  

		//update()检测飞镖落地,回收。此方法由GameModel的update()方法触发  
		public void FreeDisks() {  
			for (int i = 0; i < usingDiskList.Count; i++) {  
				if (usingDiskList[i].transform.position.y <= -8) {  
					usingDiskList[i].GetComponent<Rigidbody>().velocity = Vector3.zero;  //很重要  
					usingDiskList[i].SetActive(false);  
					unusedDiskList.Add(usingDiskList[i]);  
					usingDiskList.Remove(usingDiskList[i]);  
					i--;  

					MainSceneController.getInstance().subScore();  //打不中,扣分  
				}  
			}  
		}  

		//飞镖被击中,回收  
		public void ReuseWhenDiskBeingStruck(GameObject StruckDisk) {  
			StruckDisk.GetComponent<Rigidbody>().velocity = Vector3.zero;  //很重要  
			StruckDisk.SetActive(false);  
			unusedDiskList.Add(StruckDisk);  
			usingDiskList.Remove(StruckDisk);  
		}  

		//告知是否正在发射飞镖:若是则不能重复发射  
		public bool isLaunching() {  
			return (usingDiskList.Count > 0);  
		}  

		//初始化DiskItem  
		public void initItems(GameObject _DiskItem) {  
			DiskItem = _DiskItem;  
		}  
	}  
}  

public class DiskFactoryBC : MonoBehaviour {  
	public GameObject DiskItem;  

	void Awake() {  
		DiskFactory.getInstance().initItems(DiskItem);  
	}  
}  

4. GameModel.cs:
此类是 MainSceneController的子对象,首先负责生成游戏场景需要的GameObject,比如地面、飞碟发射器、爆炸粒子 。有两个主要方法是:获取飞碟并发射;根据鼠标位置检测是否击中飞碟。
using System.Collections;  
using System.Collections.Generic;  
using UnityEngine;  
using PlayDisks.Com;  

public class GameModel : MonoBehaviour {  
	public GameObject PlaneItem, LauncherItem, ExplosionItem;  
	public Material greenMat, redMat, blueMat;  

	private GameObject plane, launcher, explosion;  
	private MainSceneController scene;  

	private const float LAUNCH_GAP = 0.1f;  

	void Start () {  
		scene = MainSceneController.getInstance();  
		scene.setGameModel(this);  

		plane = Instantiate(PlaneItem);  
		launcher = Instantiate(LauncherItem);  
		explosion = Instantiate(ExplosionItem);  
	}  

	void Update () {  
		DiskFactory.getInstance().FreeDisks();  

	}  

	//发射飞镖  
	public void launchDisks() {  
		int roundNum = scene.getRoundNum();  
		if (!DiskFactory.getInstance().isLaunching())  
			StartCoroutine(launchDisksWithGapTime(roundNum));  
	}  
	//每个飞镖发射之间相差一段时间  
	IEnumerator launchDisksWithGapTime(int roundNum) {  
		for (int i = 0; i < roundNum; i++) {  
			GameObject Disk = DiskFactory.getInstance().getDisk();  
			Disk.transform.position = launcher.transform.position;  
			Disk.GetComponent<MeshRenderer>().material = getMaterial(roundNum);  

			Vector3 force = getRandomForce();  
			Disk.GetComponent<Rigidbody>().AddForce(force, ForceMode.Impulse);  

			yield return new WaitForSeconds(LAUNCH_GAP);  
		}  
	}  
	Vector3 getRandomForce() {  
		int x = Random.Range(-30, 31);  
		int y = Random.Range(30, 41);  
		int z = Random.Range(20, 31);  
		return new Vector3(x, y, z);  
	}  


	//击打飞镖  
	public void strikeTheDisk(Vector3 mousePos) {  
		Ray ray = Camera.main.ScreenPointToRay(mousePos);  

		RaycastHit hit;  
		if (Physics.Raycast(ray, out hit)) {  
			if (hit.collider.gameObject.tag.Equals("Disk")) {  
				createExplosion(hit.collider.gameObject.transform.position);  
				scene.addScore();  
				DiskFactory.getInstance().ReuseWhenDiskBeingStruck(hit.collider.gameObject);  
			}  
		}  
	}  
	void createExplosion(Vector3 position) {  
		explosion.transform.position = position;  
		explosion.GetComponent<ParticleSystem>().GetComponent<Renderer>().material =  
			getMaterial(scene.getRoundNum());  
		explosion.GetComponent<ParticleSystem>().Play();  
	}  

	Material getMaterial(int roundNum) {  
		switch (roundNum % 3) {  
		case 0:  
			return redMat;  
		case 1:  
			return greenMat;  
		case 2:  
			return blueMat;  
		default:  
			return redMat;  
		}  
	}  
}  



5. GameStatus.cs:
此类的存在实现了如下的功能
1.分多个 round , 每个 round 都是 n 个 trail;
2.每个 trail 的飞碟的色彩,大小;发射位置,速度,角度,每次发射飞碟数量不一;。
3.鼠标击中得分,得分按色彩、大小、速度不同计算,计分规则自由定

 
using System.Collections; 
using System.Collections.Generic; 
using UnityEngine; 
using PlayDisks.Com; 
using UnityEngine.UI; 
public class GameStatus : MonoBehaviour { 
 public GameObject canvasItem, roundTextItem, scoreTextItem, TipsTextItem; 
 private int roundNum = 1; 
 private int score = 0; 
 private const float TIPS_TEXT_SHOW_TIME = 0.8f; 
 private GameObject canvas, roundText, scoreText, TipsText; 
 private MainSceneController scene; 
 void Start () { 
  scene = MainSceneController.getInstance(); 
  scene.setGameStatus(this); 
  canvas = Instantiate(canvasItem); 
  roundText = Instantiate(roundTextItem, canvas.transform); 
  roundText.transform.Translate(canvas.transform.position); 
  roundText.GetComponent<Text>().text = "Round: " + roundNum; 
  scoreText = Instantiate(scoreTextItem, canvas.transform); 
  scoreText.transform.Translate(canvas.transform.position); 
  scoreText.GetComponent<Text>().text = "Score:  " + score + " / " + (roundNum * 100); 
  TipsText = Instantiate(TipsTextItem, canvas.transform); 
  TipsText.transform.Translate(canvas.transform.position); 
  showTipsText(); 
 } 
 void Update () { 
 } 
 public int getRoundNum() { 
  return roundNum; 
 } 
 void addRoundNum() { 
  roundNum++; 
  roundText.GetComponent<Text>().text = "Round: " + roundNum; 
 } 
 public int getScore() { 
  return score; 
 } 
 //得分,+100 
 public void addScore() { 
  score += 100; 
  scoreText.GetComponent<Text>().text = "Score:  " + score + " / " + (roundNum * 100); 
  checkScore(); 
 } 
 //扣分,-100 
 public void subScore() { 
  score = score >= 100 ? score - 100 : 0; 
  scoreText.GetComponent<Text>().text = "Score:  " + score + " / " + (roundNum * 100); 
 } 
 //检测分数是否已满 
 void checkScore() { 
  if (score >= roundNum * 100) {  //可以下一关 
   addRoundNum(); 
   resetScore(); 
   showTipsText(); 
  } 
 } 
 void resetScore() { 
  score = 0; 
  scoreText.GetComponent<Text>().text = "Score:  " + score + " / " + (roundNum * 100); 
 } 
 void showTipsText() { 
  TipsText.GetComponent<Text>().text = "Round " + roundNum + " !"; 
  TipsText.SetActive(true); 
  StartCoroutine(waitForSomeAndDisappearTipsText()); 
 } 
 IEnumerator waitForSomeAndDisappearTipsText() { 
  yield return new WaitForSeconds(TIPS_TEXT_SHOW_TIME); 
  TipsText.SetActive(false); 
 } 

再由上节课学到的知识,我又做了一个skybox是界面更加美观一些。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值