大家好,欢迎来到IT知识分享网。
目录
一、游戏内容与要求
游戏规则:
- 游戏有3个round,每个round发射10次trial.
- 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制。
- 每个 trial 的飞碟有随机性,总体难度随 round 上升。
- 鼠标点中飞碟得分,每个飞碟的分数为 b a s e S c o r e ∗ s p e e d R a t e baseScore*speedRate baseScore∗speedRate。其中
baseScore
计算为红色3分、绿色2分、蓝色1分(颜色与飞碟大小对应);speedRate
计算为 ( s p e e d X + s p e e d Y ) / 2 (speedX + speedY) / 2 (speedX+speedY)/2.
游戏要求:
- 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的。
- 近可能使用前面 MVC 结构实现人机交互与游戏模型分离。
二、游戏设计
- 游戏架构设计
依旧采用MVC架构以及动作分离设计,同时将场景控制器DiskSceneController
、飞碟工厂DiskFactory
、飞碟运动管理器DiskActionManager
设置为场景单实例。
因为增加了场景单实例的应用,删除了场景、用户交互接口。组件无需再通过接口,可以直接通过获取某个对象的实例进行调用。 - 对象工厂设计
飞碟对象工厂DiskFactory
是一个场景单实例类。当无闲置对象时从预制件中实例化一个新的对象;否则获取闲置对象,赋予该对象新的属性,从而减少对象的创建与销毁。该工厂封装了三个方法:
GetDisk() : 获取飞碟对象
FreeDisk() : 回收飞碟对象
InitUsedDisk() : 重置场景时回收飞碟对象
以下给出伪代码:
getDisk(ruler) BEGIN 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 END FreeDisk(diskobject) BEGIN Find disk in used list IF (not found) THEN THROW exception Move disk from used to free list END InitUsedDisk() BEGIN WHILE(used list has disk) Free(diskobject) ENDWHILE
三、游戏代码实现
SceneDirector
、SSActionManager
等导演类与基类在《牧师与魔鬼》游戏中已经介绍过,不在此处放置代码。
Singleton
Singleton
作为模板类。需要设置为场景单实例的对象需要继承该类,随后可以通过Singleton<T>.Instance
获取对象。
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; } } }
Ruler与Disk
Ruler
类定义了飞碟工厂生产飞碟的规则,Disk
类定义了飞碟的属性。
public class Ruler {
public int color; public Vector3 size; public Vector3 beginPos; public Vector3 speed; } public class Disk : MonoBehaviour {
public int color; public Vector3 speed; public Vector3 size; }
DiskFactory
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class DiskFactory : Singleton<DiskFactory> {
private List<Disk> usedDisk; private List<Disk> freeDisk; // Start is called before the first frame update void Start() {
usedDisk = new List<Disk>(); freeDisk = new List<Disk>(); } public GameObject getDisk(Ruler ruler){
GameObject disk; //freeDisk中有disk int freeCount = freeDisk.Count; if(freeCount > 0){
disk = freeDisk[freeCount - 1].gameObject; freeDisk.RemoveAt(freeCount - 1); } //freeDisk中没有disk,从预制中加载 else{
disk = GameObject.Instantiate(Resources.Load<GameObject>("Prefabs/Disk"), Vector3.zero, Quaternion.identity); //disk.AddComponent(typeof(Disk)); } //设置disk的属性 if(ruler.color == 1){
disk.GetComponent<MeshRenderer>().material.color = Color.blue; disk.transform.localScale = ruler.size; disk.GetComponent<Disk>().color = 1; disk.GetComponent<Disk>().size = ruler.size; } else if(ruler.color == 2){
disk.GetComponent<MeshRenderer>().material.color = Color.green; disk.transform.localScale = ruler.size; disk.GetComponent<Disk>().color = 2; disk.GetComponent<Disk>().size = ruler.size; } else if(ruler.color == 3){
disk.GetComponent<MeshRenderer>().material.color = Color.red; disk.transform.localScale = ruler.size; disk.GetComponent<Disk>().color = 3; disk.GetComponent<Disk>().size = ruler.size; } disk.transform.position = ruler.beginPos; disk.GetComponent<Rigidbody>().velocity = ruler.speed; disk.GetComponent<Disk>().speed = ruler.speed; disk.SetActive(true); usedDisk.Add(disk.GetComponent<Disk>()); return disk; } public void FreeDisk(GameObject disk){
foreach(Disk d in usedDisk){
if(d.gameObject.GetInstanceID() == disk.GetInstanceID()){
disk.SetActive(false); usedDisk.Remove(d); freeDisk.Add(d); break; } } } public void InitUsedDisk(){
while(usedDisk.Count != 0){
FreeDisk(usedDisk[0].gameObject); } } // Update is called once per frame void Update() {
} }
DiskMoveAction
DiskMoveAction
创建了飞碟的运动。由于在预制件中将飞碟设置为刚体,飞碟工厂生产飞碟时设置了初速度,故不再需要对其添加额外的运动。仅需要为其设置回调函数:当飞碟下落至某个高度时通知运动管理器回收该飞碟。
public class DiskMoveAction : SSAction {
public static DiskMoveAction GetSSAction(){
DiskMoveAction action = ScriptableObject.CreateInstance<DiskMoveAction> (); return action; } public override void Update () {
if (this.transform.position.y < -2) {
this.callback.SSActionEvent (this); } } public override void Start () {
} }
DiskActionManager
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DiskActionManager : SSActionManager, ISSActionCallback {
private DiskSceneController sceneController; // Update is called once per frame protected new void Update () {
base.Update (); } #region ISSActionCallback implementation public void SSActionEvent (SSAction source, SSActionEventType events = SSActionEventType.Competeted, int intParam = 0, string strParam = null, Object objectParam = null) {
DiskFactory df = Singleton<DiskFactory>.Instance; df.FreeDisk(source.gameobject); } #endregion }
ScoreRecorder
ScoreRecorder
用于记录、管理游戏分数。
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class ScoreRecorder {
public int score; public ScoreRecorder(){
score = 0; } // 记录分数,颜色基础分数*速度倍数 public void record(Disk d){
score += d.color * (int)((Math.Abs(d.speed.x) + d.speed.y) / 2); } // 重置 public void resetScore(){
score = 0; } }
DiskSceneController
DiskSceneController
是飞碟游戏的场景控制器,集成了飞碟工厂、飞碟动作管理器、分数记录器等实例,并在此完成游戏的主要逻辑。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DiskSceneController : Singleton<DiskSceneController> {
public DiskActionManager actionManager; public ScoreRecorder sr; public DiskFactory df; public bool isPlaying; private int round, trial; private int[] disk_num_range = {
2, 2, 3, 3, 3, 4, 4, 4, 5, 6}; private int[] disk_speed_range = {
5, 6, 6, 7, 7, 7, 8, 8, 9, 9}; private float time_interval; void Awake(){
SceneDirector director = SceneDirector.getInstance(); director.currentSceneController = this; actionManager = Singleton<DiskActionManager>.Instance; df = Singleton<DiskFactory>.Instance; sr = new ScoreRecorder(); } void Init(){
sr.resetScore(); isPlaying = true; round = 1; trial = 0; time_interval = 0f; } //发射飞碟 void launchDisk(){
if(trial >= 10){
round++; trial = 0; } if(round > 3){
isPlaying = false; return; } //根据随机生成的飞碟数创建飞碟 int disk_num = Random.Range(1, disk_num_range[trial]) + round - 1; for(int i = 0; i < disk_num; i++){
int flag = Random.Range(0, 2) == 0 ? -1 : 1; int speedx = Random.Range(4, disk_speed_range[trial]) + round + 10; int speedy = Random.Range(4, disk_speed_range[trial]) + round - 1; Ruler ruler = new Ruler(); ruler.color = Random.Range(1, 4); ruler.speed = new Vector3(speedx * (-flag), speedy, 0); ruler.size = new Vector3(4-ruler.color, 0.2f, 4-ruler.color); ruler.beginPos = new Vector3(18 * flag, Random.Range(6, 10), 0); GameObject disk = df.getDisk(ruler); DiskMoveAction move = DiskMoveAction.GetSSAction(); actionManager.RunAction(disk, move, actionManager); } //round与trial控制 trial++; } //打飞碟 public void hitDisk(Vector3 pos){
Camera ca = Camera.main; Ray ray = ca.ScreenPointToRay(pos); RaycastHit hit; if(Physics.Raycast(ray, out hit)){
if(hit.collider.gameObject != null){
GameObject d = hit.collider.gameObject; //降低被击中飞碟y坐标值,以触发其运动的回调函数进行回收 d.transform.position = new Vector3(0, -5, 0); sr.record(d.GetComponent<Disk>()); } } } public int getRound(){
return round; } public int getTrial(){
return trial; } public void resetGame(){
Init(); df.InitUsedDisk(); } public void GameOver(){
SceneDirector.getInstance ().NextScene (); } // Start is called before the first frame update void Start() {
Init(); } // Update is called once per frame void Update() {
if(isPlaying){
//每2秒发射一次飞碟 time_interval += Time.deltaTime; if(time_interval > 2f){
time_interval = 0f; launchDisk(); } if(Input.GetButtonDown("Fire1")){
hitDisk(Input.mousePosition); } } } }
DiskUserGUI
在该类中完成用户交互界面的绘制。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DiskUserGUI : MonoBehaviour {
private DiskSceneController controller; // Start is called before the first frame update void Start() {
controller = Singleton<DiskSceneController>.Instance; } // Update is called once per frame void OnGUI() {
float width = Screen.width / 5; float height = Screen.height / 12; if(GUI.Button(new Rect(0, 0, width, height), "退出游戏")){
controller.GameOver(); } if(GUI.Button(new Rect(width, 0, width, height), "重新开始")){
controller.resetGame(); } if(!controller.isPlaying){
GUI.Box(new Rect(width*2, height*2, width, height), "游戏结束!\n最终分数:" + controller.sr.score); } GUIStyle style = new GUIStyle(); style.fontSize = 30; style.normal.textColor = Color.black; GUI.Label(new Rect(width*2, 0, width*2, height), "Round:" + controller.getRound() + " Trial:" + controller.getTrial(), style); GUI.Label(new Rect(width*4, 0, width, height), "Score:" + controller.sr.score, style); } }
四、演示视频
简单鼠标打飞碟游戏
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/145079.html