飞碟游戏
游戏内容:
1.游戏有 n 个 round,每个 round 都包括10 次 trial;
2.每个 trial 的飞碟的色彩,大小;发射位置,速度,角度,受该 round 的 ruler 控制;
3.每个 trial 的飞碟有随机性,总体难度随 round 上升;
4.鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
游戏涉及的知识点:
1.世界与屏幕坐标、射线与碰撞、动态修改shader
2.支持mono的单实例、游戏对象工厂、扩展游戏对象属性与行为的方法
游戏内容:
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.鼠标击中得分,得分按色彩、大小、速度不同计算,计分规则自由定
2.每个 trail 的飞碟的色彩,大小;发射位置,速度,角度,每次发射飞碟数量不一;。
3.鼠标击中得分,得分按色彩、大小、速度不同计算,计分规则自由定
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using PlayDisks.Com;
using UnityEngine.UI;
using System.Collections.Generic;
using UnityEngine;
using PlayDisks.Com;
using UnityEngine.UI;
public class GameStatus : MonoBehaviour {
public GameObject canvasItem, roundTextItem, scoreTextItem, TipsTextItem;
public GameObject canvasItem, roundTextItem, scoreTextItem, TipsTextItem;
private int roundNum = 1;
private int score = 0;
private int score = 0;
private const float TIPS_TEXT_SHOW_TIME = 0.8f;
private GameObject canvas, roundText, scoreText, TipsText;
private MainSceneController scene;
private MainSceneController scene;
void Start () {
scene = MainSceneController.getInstance();
scene.setGameStatus(this);
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;
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);
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();
}
TipsText.transform.Translate(canvas.transform.position);
showTipsText();
}
void Update () {
}
public int getRoundNum() {
return roundNum;
}
return roundNum;
}
void addRoundNum() {
roundNum++;
roundText.GetComponent<Text>().text = "Round: " + roundNum;
}
roundNum++;
roundText.GetComponent<Text>().text = "Round: " + roundNum;
}
public int getScore() {
return score;
}
return score;
}
//得分,+100
public void addScore() {
score += 100;
scoreText.GetComponent<Text>().text = "Score: " + score + " / " + (roundNum * 100);
checkScore();
}
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);
}
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();
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);
}
}
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);
}
}
}
}