3D游戏设计作业4
编写一个简单的鼠标打飞碟(Hit UFO)游戏
1,作业要求:
游戏内容要求:
1,游戏有 n 个 round,每个 round 都包括10 次 trial;
2,每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制;
3,每个 trial 的飞碟有随机性,总体难度随 round 上升;
4,鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可*设定。
游戏的要求:
1,使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
2,尽可能使用前面 MVC 结构实现人机交互与游戏模型分离
2,设计思路
1,制作飞碟
选用Sphere和Capsule来制作飞碟
如图所示:
2,分析singleton模板类
Singleton模板类通过使用泛型来为每一个需要使用单例模式的类创建一个且唯一的一个对象实例,避免单例模式中的代码重复,节省了大量资源。
代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Singleton<T> : MonoBehaviour where T : MonoBehaviour {
protected static T instance;
public static T Instance
{
get
{
if(instance == null)
{
instance = (T)FindObjectOfType(typeof(T));
if(instance == null)
{
Debug.LogError("An instance of " + typeof(T) + " is needed in the scene, but there is none.");
}
}
return instance;
}
}
}
3,具体设计的类
很多类是上一次作业已经设计过的,如:
导演类Director、接口场景类ISceneController、接口类IUserAction、所有动作的基础类SSAction、组合动作管理类SSActionManager、动作事件接口类ISSActionCallback、事件管理类CCActionManager、*的控制类FirstSceneController、用户界面类UserGUI。
这一次作业增加的类如下:
飞碟数据类DiskData,说明当前飞碟的状态,用于描述飞碟。
飞碟飞行动作类CCFlyAction。
飞碟工厂类DiskFactory,用于制造和销毁飞碟的工厂。
模板类Singleton。
记分员ScoreRecorder。
3,部分代码
ISceneController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public interface ISceneControl {
void loadResources();
}
IUserAction.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum GameState { ROUND_START, ROUND_FINISH, RUNNING, PAUSE, START, FUNISH}
public interface IUserAction{
GameState getGameState();
void setGameState(GameState gameState);
int getScore();
void hit(Vector3 pos);
}
DiskData.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiskData : MonoBehaviour {
private Vector3 size;
private Color color;
private float speed;
private Vector3 direction;
public DiskData() { }
public Vector3 getSize()
{
return size;
}
public float getSpeed()
{
return speed;
}
public Vector3 getDirection()
{
return direction;
}
public Color getColor()
{
return color;
}
public void setDiskData(Vector3 size, Color color, float speed, Vector3 direction)
{
this.size = size;
this.color = color;
this.speed = speed;
this.direction = direction;
}
}
DiskFactory.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DiskFactory : MonoBehaviour {
public GameObject diskPrefab;
public List<DiskData> used = new List<DiskData>();
public List<DiskData> free = new List<DiskData>();
private void Awake()
{
diskPrefab = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/disk"), Vector3.zero, Quaternion.identity);
diskPrefab.SetActive(false);
}
public GameObject getDisk(int round)
{
GameObject disk = null;
if(free.Count > 0)
{
disk = free[0].gameObject;
free.Remove(free[0]);
}
else
{
disk = GameObject.Instantiate<GameObject>(diskPrefab, Vector3.zero, Quaternion.identity);
disk.AddComponent<DiskData>();
}
int start;
switch (round)
{
case 0: start = 0; break;
case 1: start = 100; break;
default: start = 200; break;
}
int selectColor = Random.Range(start, round * 499);
round = selectColor / 250;
DiskData diskData = disk.GetComponent<DiskData>();
Renderer renderer = disk.GetComponent<Renderer>();
Renderer childRenderer = disk.transform.GetChild(0).GetComponent<Renderer>();
float ranX = Random.Range(-1, 1) < 0 ? -1.2f : 1.2f;
Vector3 direction = new Vector3(ranX, 1, 0);
switch (round)
{
case 0:
diskData.setDiskData(new Vector3(1.35f, 1.35f, 1.35f), Color.white, 4.0f, direction);
renderer.material.color = Color.white;
childRenderer.material.color = Color.white;
break;
case 1:
diskData.setDiskData(new Vector3(1f, 1f, 1f), Color.gray, 6.0f, direction);
renderer.material.color = Color.gray;
childRenderer.material.color = Color.gray;
break;
case 2:
diskData.setDiskData(new Vector3(0.7f, 0.7f, 0.7f), Color.black, 8.0f, direction);
renderer.material.color = Color.black;
childRenderer.material.color = Color.black;
break;
}
used.Add(diskData);
diskData.name = diskData.GetInstanceID().ToString();
disk.transform.localScale = diskData.getSize();
return disk;
}
public void freeDisk(GameObject disk)
{
DiskData temp = null;
foreach (DiskData i in used)
{
if (disk.GetInstanceID() == i.gameObject.GetInstanceID())
{
temp = i;
}
}
if (temp != null)
{
temp.gameObject.SetActive(false);
free.Add(temp);
used.Remove(temp);
}
}
}
SSAction.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SSAction : ScriptableObject {
public bool enable = false;
public bool destroy = false;
public GameObject gameObject { set; get; }
public Transform transform { set; get; }
public ISSActionCallback callback { set; get; }
protected SSAction() { }
public virtual void Start()
{
throw new System.NotImplementedException();
}
public virtual void Update()
{
throw new System.NotImplementedException();
}
public void reset()
{
enable = false;
destroy = false;
gameObject = null;
transform = null;
callback = null;
}
}
UserGUI.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class UserGUI : MonoBehaviour {
private IUserAction action;
bool isFirst = true;
GUIStyle red;
GUIStyle black;
// Use this for initialization
void Start () {
action = Director.getInstance().current as IUserAction;
black = new GUIStyle("button");
black.fontSize = 20;
red = new GUIStyle();
red.fontSize = 30;
red.fontStyle = FontStyle.Bold;
red.normal.textColor = Color.red;
red.alignment = TextAnchor.UpperCenter;
}
private void OnGUI()
{
if (action.getGameState() == GameState.FUNISH)
{
GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 150, 200, 100), action.getScore() >= 30 ? "你胜利了" : "你失败了", red);
if(GUI.Button(new Rect(Screen.width / 2 - 60, Screen.height / 2 - 50, 120, 40), "重新开始", black))
{
SceneManager.LoadScene("DiskAttack");
}
return;
}
Rect rect = new Rect(Screen.width / 2 - 100, 0, 200, 40);
Rect rect2 = new Rect(Screen.width / 2 - 45, 60, 120, 40);
if (Input.GetButtonDown("Fire1") && action.getGameState() != GameState.PAUSE)
{
Vector3 pos = Input.mousePosition;
action.hit(pos);
}
if (!isFirst)
{
GUI.Label(rect, "你的分数: " + action.getScore().ToString(), red);
}
else
{
GUIStyle blackLabel = new GUIStyle();
blackLabel.fontSize = 16;
blackLabel.normal.textColor = Color.black;
GUI.Label(new Rect(Screen.width / 2 - 250, 120, 500, 200), "一共3个关卡,每一个关卡有10个飞碟,飞碟" +
"的颜色是不一样的\n如果攻击白色的飞碟,你会得到1分。如果攻击灰色的飞碟\n" +
"则会得到2分,如果攻击黑色的飞碟(速度最快),你会得到4分。\n" +
"游戏结束时得到超过30分就胜利了!", blackLabel);
}
if (action.getGameState() == GameState.RUNNING && GUI.Button(rect2, "暂停", black))
{
action.setGameState(GameState.PAUSE);
}
else if(action.getGameState() == GameState.PAUSE && GUI.Button(rect2, "继续", black))
{
action.setGameState(GameState.RUNNING);
}
if (isFirst && GUI.Button(rect2, "开始", black))
{
isFirst = false;
action.setGameState(GameState.ROUND_START);
}
if(!isFirst && action.getGameState() == GameState.ROUND_FINISH && GUI.Button(rect2, "下一关卡", black))
{
action.setGameState(GameState.ROUND_START);
}
}
}
上一篇: Java数据结构与算法——深度优先搜索与广度优先搜索
下一篇: 3D游戏作业——游戏智能