unity:打飞碟小游戏

2024-01-21 22:50
文章标签 unity 小游戏 飞碟

本文主要是介绍unity:打飞碟小游戏,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这里写目录标题

  • 游戏开发要求
  • 游戏规则
  • 工厂与对象池理解
  • UML图
  • 代码介绍
    • action
    • action不多做讲解,可以参考之前博客的牧师与魔鬼动作分离版的讲解
    • config
    • controller
    • factory
    • model
    • view
  • 创建预制&&配置ruler&&配置天空盒&&挂载脚本
  • 视频演示
  • 代码仓库

游戏开发要求

  • 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
  • 近可能使用前面 MVC 结构实现人机交互与游戏模型分离
  • 必须使用对象池管理飞碟对象。
  • 建议使用 ScriptableObject 配置不同的飞碟(方便配置修改)
  • 建议使用物理引擎管理飞碟飞行路径。

使用 UML 图、伪代码辅助表达对象池的原理,对象配置方法,以及你的设计

游戏规则

  • 游戏有 n 个 round,每个 round 都包括10 次 trial。
  • 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制。
  • 每个 trial 的飞碟有随机性,总体难度随 round 上升。
  • 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
diskscore
2
4
6
绿8

工厂与对象池理解

工厂方法,即类一个方法能够得到一个对象实例,使用者不需要知道该实例如何构建、初始化等细节。

好处:

  • 游戏对象的创建与销毁高成本,必须减少销毁次数。
  • 屏蔽创建与销毁的业务逻辑,使程序易于扩展

在这里插入图片描述

UML图

在这里插入图片描述

代码介绍

代码分为下面几个模块

遵循MVC架构和cocos2d的设计思想

action

action不多做讲解,可以参考之前博客的牧师与魔鬼动作分离版的讲解

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public enum SSActionEventType : int { Started, Competeted }
public interface ISSActionCallback
{/// <summary>/// when an action has been excuted, call the callback function/// </summary>/// <param name="source">action has been excuted</param>/// <param name="events"></param>/// <param name="intParam"></param>/// <param name="strParam"></param>/// <param name="objectParam"></param>public void SSActionEvent(SSAction source,SSActionEventType events = SSActionEventType.Competeted,int intParam = 0,string strParam = null,GameObject objectParam = null);}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SSAction : ScriptableObject
{/// <summary>/// if enable, then will call update per frame/// </summary>public bool enable = true;/// <summary>/// if destory, will be removed after a frame/// </summary>public bool destroy = false;public GameObject gameobject { get; set; }public Transform transform { get; set; }public ISSActionCallback callback { get; set; }protected SSAction() { }// Start is called before the first frame updatepublic virtual void Start(){throw new System.NotImplementedException();}// Update is called once per framepublic virtual void Update(){throw new System.NotImplementedException();}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SSActionManager : MonoBehaviour
{private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();private List<SSAction> waitingAdd = new List<SSAction> ();private List<int> waitingDelete = new List<int>();// Start is called before the first frame updateprotected void Start(){}// Update is called once per frameprotected void Update(){// add to actionsforeach(SSAction ac in waitingAdd){actions[ac.GetInstanceID()] = ac;}waitingAdd.Clear();//run actionsforeach(KeyValuePair<int, SSAction> kv in actions){SSAction ac = kv.Value;if(ac.destroy){waitingDelete.Add(ac.GetInstanceID());}else if(ac.enable){ac.Update();//call action update manually}}//delete actionsforeach(int key in waitingDelete){SSAction ac = actions[key];actions.Remove(key);Destroy(ac);}waitingDelete.Clear();}public void RunAction(GameObject gameobject, SSAction action, ISSActionCallback manager){action.gameobject = gameobject;//add action to gameobjectaction.transform = gameobject.transform;//ser action's transformaction.callback = manager;//set action's callbackwaitingAdd.Add(action);action.Start();//not monobehavior thus call start mannually}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class NPhysicalDiskAction : SSAction
{float gravity;float time;float speed;Vector3 direction;// builder, get specific actions success SSActionpublic static NPhysicalDiskAction GetSSAction(Vector3 direction, float speed){NPhysicalDiskAction action = ScriptableObject.CreateInstance<NPhysicalDiskAction>();action.gravity = 9.8f;action.time = 0;action.speed = speed;action.direction = direction;return action;}// Start is called before the first frame updatepublic override void Start(){gameobject.GetComponent<Rigidbody>().isKinematic = true;}// Update is called once per framepublic override void Update(){// disk move 运动合成,transform is the one of scriptable object,// which will be set equal to gameobject which excecutes this action. transform.Translate(Vector3.down * gravity * time * Time.deltaTime);transform.Translate(direction * speed * Time.deltaTime);time += Time.deltaTime;//Debug.Log("postion of the disk"+gameobject.name+" is "+gameobject.transform.position);// 飞碟到达屏幕底部回调Camera mainCamera = Camera.main; // 获取主摄像机float cameraHeight = mainCamera.orthographicSize * 2f; // 获取相机的视图高度// 计算相机视图的上下边界坐标float upperBoundary = mainCamera.transform.position.y + cameraHeight / 2f;float lowerBoundary = mainCamera.transform.position.y - cameraHeight / 2f;//Debug.Log("Upper Boundary: " + upperBoundary);//Debug.Log("Lower Boundary: " + lowerBoundary);if (this.transform.position.y < lowerBoundary-100 || this.transform.position.y > upperBoundary+100){// action destorythis.destroy = true;this.enable = false;// action callbackthis.callback.SSActionEvent(this);//source as this}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class PhysicalDiskAction : SSAction
{private float speed;private Vector3 direction;// builderpublic static PhysicalDiskAction GetSSAction(Vector3 direction, float speed){PhysicalDiskAction ac = ScriptableObject.CreateInstance<PhysicalDiskAction>();ac.speed = speed;ac.direction = direction;return ac;}// Start is called before the first frame updatepublic override void Start(){// add clash pysical effectgameobject.GetComponent<Rigidbody>().isKinematic = false;// add init speedgameobject.GetComponent<Rigidbody>().velocity = speed * direction;}// Update is called once per framepublic override void Update(){//Debug.Log("postion of the disk"+gameobject.name+" is "+gameobject.transform.position);// 飞碟到达屏幕底部回调Camera mainCamera = Camera.main; // 获取主摄像机float cameraHeight = mainCamera.orthographicSize * 2f; // 获取相机的视图高度// 计算相机视图的上下边界坐标float upperBoundary = mainCamera.transform.position.y + cameraHeight / 2f;float lowerBoundary = mainCamera.transform.position.y - cameraHeight / 2f;//Debug.Log("Upper Boundary: " + upperBoundary);//Debug.Log("Lower Boundary: " + lowerBoundary);if (this.transform.position.y < lowerBoundary - 100 || this.transform.position.y > upperBoundary + 100){// action destorythis.destroy = true;this.enable = false;// action callbackthis.callback.SSActionEvent(this);//source as this}}
}
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;public class DiskActionManager : SSActionManager, ISSActionCallback
{public FirstController firstController;public void ShootDisk(GameObject disk, float speed, Vector3 direction,PhysiclaType type){SSAction ac;// run actionif (type==  PhysiclaType.noPhysical){ac = NPhysicalDiskAction.GetSSAction(direction, speed);}else if (type == PhysiclaType.pysical){ac = PhysicalDiskAction.GetSSAction(direction, speed);}else{throw new NotImplementedException();}RunAction(disk, ac, this);}protected new void Start(){// get fisrt controllerfirstController = (FirstController)Director.GetInstance().CurrentSceneController;// set action manager as thisfirstController.actionManager = this;}// specific manager do callbackpublic void SSActionEvent(SSAction source,SSActionEventType events = SSActionEventType.Competeted,int intParam = 0,string strParam = null,GameObject objectParam = null){//回收飞碟//source is the class success the SSAction, in this case is obj of NphysicalDiskAction ClassSingleton<RoundController>.Instance.FreeDisk(source.gameobject);}
}

config


using System;
using UnityEngine;[CreateAssetMenu(menuName ="create ruler config")]
public class Ruler : ScriptableObject
{// 对局属性public int trailIndex;public int roundIndex;public int SumRoundNum;public int SumTrailNum;public int[] diskNumPerRound;//avg disk num per roundpublic float[] shootDeltaTimePerRound;//发射间隔// 飞碟属性public DiskFeature diskFeature;[NonSerialized]public Vector3 startPos;[NonSerialized]public Vector3 startDir;}

controller

Director 导演类,管理场记

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;public class Director : System.Object
{static Director _instance;// 关联场记实例public ISceneController CurrentSceneController { get; set; }// 获取当前的场记public static Director GetInstance(){if (_instance == null){_instance = new Director();}return _instance;}public static void ReloadCurrentScene(){int currentSceneIndex = SceneManager.GetActiveScene().buildIndex;SceneManager.LoadScene(currentSceneIndex);}
}

ISceneController场记类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public interface ISceneController
{void LoadResources();
}

IUserAction定义了用户的动作

using System.Collections;
using System.Collections.Generic;
using UnityEngine;// 抽象接口,定义用户的行为
public interface IUserAction
{void Restart();
}

FirstController 第一个场记,继承ISceneController

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/* 游戏状态,0为准备进行,1为正在进行游戏,2为结束 */
public enum GameState
{Ready = 0, Playing = 1, GameOver = 2
};public class FirstController : MonoBehaviour, ISceneController
{public DiskActionManager actionManager;public RoundController roundController;public UserGUI userGUI;public GameState gameState;// ruler configpublic Ruler rulerConfig;public void LoadResources(){Director.GetInstance().CurrentSceneController = this;roundController = gameObject.AddComponent<RoundController>();Debug.Log("add roundController Component");userGUI = gameObject.AddComponent<UserGUI>();Debug.Log("add UserGUI");actionManager = gameObject.AddComponent<DiskActionManager>();Debug.Log("add DiskActionManager");gameState = (int)GameState.Ready;}public void Restart(){Director.ReloadCurrentScene();}public void JudgeResultCallBack(string result){}void Awake(){LoadResources();}void Start(){}void Update(){}public void GameOver(){gameState = GameState.GameOver;userGUI.SetGameState(GameState.GameOver);}public void SetGameMode(PhysiclaType type){roundController.phyType= type;}
}

RoundController 控制飞碟对象的运动类型为运动学运动或物理运动,并根据一定规则请求飞碟工厂生成相应的飞碟对象,控制游戏飞碟对象的发射

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;public enum PhysiclaType
{pysical,noPhysical
}public class RoundController : MonoBehaviour
{DiskActionManager actionManager;ScoreRecorder scoreRecorder;FirstController firstController;Ruler ruler;float time = 0;public PhysiclaType phyType;public void LaunchDisk(){// 使飞碟飞入位置尽可能分开,从不同位置飞入使用的数组int[] beginPosY = new int[10];float beginPosX;// shoot the disks for each trailint diskNum = ruler.diskNumPerRound[ruler.roundIndex] / ruler.SumTrailNum;diskNum += Random.Range(0, 5);// random num per trailfor (int i = 0; i < diskNum; ++i){// 获取随机数int randomNum = Random.Range(1, 4);// 飞碟速度随回合数增加而变快ruler.diskFeature.speed = randomNum * (ruler.roundIndex + 2);// 根据随机数选择飞碟颜色randomNum = Random.Range(1, 4);if (randomNum == 1){ruler.diskFeature.color = DiskFeature.colors.red;}else if (randomNum == 2){ruler.diskFeature.color = DiskFeature.colors.green;}else{ruler.diskFeature.color = DiskFeature.colors.blue;}// 重新选取随机数,并根据随机数选择飞碟的大小ruler.diskFeature.size = Random.Range(1, 4);// 重新选取随机数,并根据随机数选择飞碟飞入的方向randomNum = Random.Range(0, 2);Camera mainCamera = Camera.main;float cameraW = mainCamera.orthographicSize * 2f * Camera.main.aspect;if (randomNum == 1){ruler.startDir = new Vector3(3, 1, 0);beginPosX= mainCamera.transform.position.x - cameraW / 2f;}else{ruler.startDir = new Vector3(-3, 1, 0);beginPosX = mainCamera.transform.position.x + cameraW / 2f;}// 重新选取随机数,并使不同飞碟的飞入位置尽可能分开int iterNum = 0;do{// case if infinite iteriterNum++;if (iterNum>=100){int ii;for (ii = 0; ii < beginPosY.Length; ii++){beginPosY.SetValue(0, ii);}}randomNum = Random.Range(0, 10);} while (beginPosY[randomNum] != 0);beginPosY[randomNum] = 1;ruler.startPos = new Vector3(beginPosX, -0.2f * randomNum, 0);// 根据ruler从工厂中生成一个飞碟GameObject disk = Singleton<DiskFactory>.Instance.GetDisk(ruler);// 设置飞碟的飞行动作actionManager.ShootDisk(disk, ruler.diskFeature.speed, ruler.startDir,phyType);}}/// <summary>/// free the gameobject that loaded the action/// </summary>/// <param name="obj">obj is the one load the action</param>public void FreeDisk(GameObject disk){// call factory to free the diskSingleton<DiskFactory>.Instance.FreeDisk(disk);}// 用户点击碰撞public void Hit(Vector3 position){Camera camera = Camera.main;Ray ray = camera.ScreenPointToRay(position);RaycastHit[] hits;hits = Physics.RaycastAll(ray);for (int i = 0; i < hits.Length; i++){RaycastHit hit = hits[i];if (hit.collider.gameObject != null){// 把击中的飞碟移出屏幕,触发回调释放hit.collider.gameObject.transform.position = new Vector3(0, -6, 0);// 记录飞碟得分scoreRecorder.Record(hit.collider.gameObject.GetComponent<Disk>());// 显示当前得分UserGUI userGUI = Singleton<UserGUI>.Instance;userGUI.SetScore(scoreRecorder.GetScore());}}}// Start is called before the first frame updatevoid Start(){Debug.Log("gameobject of roundcontroller is:" + this.gameObject);actionManager = gameObject.AddComponent<DiskActionManager>();scoreRecorder = gameObject.AddComponent<ScoreRecorder>();firstController = Director.GetInstance().CurrentSceneController as FirstController;ruler = firstController.rulerConfig;gameObject.AddComponent<DiskFactory>();ruler.roundIndex= 0;ruler.trailIndex= 0;Debug.Log("ruler config:");Debug.Log(JsonUtility.ToJson(ruler));phyType = PhysiclaType.noPhysical;Debug.Log("round controller's phytype is:"+phyType);}// Update is called once per framevoid Update(){// when playingif (firstController.gameState == GameState.Playing){//update usergui round indexUpdateRoundInfo();//Debug.Log("round cont is playing update--");//Debug.Log("roundIndex" + ruler.roundIndex + "trailIndex" + ruler.trailIndex);time += Time.deltaTime;float shootDeltaTime = ruler.shootDeltaTimePerRound[ruler.roundIndex];// 发射一次飞碟(trial)if (time > shootDeltaTime){// reset timetime = 0;if (ruler.trailIndex<ruler.SumTrailNum && ruler.roundIndex<ruler.SumRoundNum){// 发射飞碟Debug.LogFormat("call LaunchDisk at round controller--at round {0} trail {1}", ruler.roundIndex, ruler.trailIndex);LaunchDisk();ruler.trailIndex++;// 回合加一,重新生成飞碟数组if (ruler.trailIndex == ruler.SumTrailNum){ruler.trailIndex = 0;ruler.roundIndex++;}}// 否则游戏结束,提示重新进行游戏else{firstController.GameOver();}}}}private void UpdateRoundInfo(){Singleton<UserGUI>.Instance.SetIndex(ruler.roundIndex, ruler.trailIndex);Singleton<UserGUI>.Instance.SetRTNum(ruler.SumRoundNum, ruler.SumTrailNum);Singleton<UserGUI>.Instance.SetPhyMode(phyType);}}

记分类根据记分规则,对被点击中的飞碟得分进行记录

using UnityEngine;public class ScoreRecorder : MonoBehaviour
{private int score;public ScoreRecorder(){ score = 0; }public ScoreRecorder(int score){ this.score = score; }public int GetScore(){return score;}// record the scorepublic void Record(Disk disk){// 飞碟大小为1得3分,大小为2得2分,大小为3得1分int diskSize = disk.diskFeature.size;switch (diskSize){case 1:score += 3;break;case 2:score += 2;break;case 3:score += 1;break;default: break;}// 速度越快分就越高score += disk.diskFeature.speed;// 颜色为红色得1分,颜色为黄色得2分,颜色为蓝色得3分DiskFeature.colors diskColor = disk.diskFeature.color;if (diskColor == DiskFeature.colors.red){score += 1;}else if (diskColor == DiskFeature.colors.yellow){score += 2;}else if (diskColor == DiskFeature.colors.blue){score += 3;}}/* 重置分数,设为0 */public void Reset(){score = 0;}// Use this for initializationvoid Start(){}// Update is called once per framevoid Update(){}
}

factory

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 = FindObjectOfType(typeof(T)) as T;if (instance == null){Debug.LogError("no instance of type " + typeof(T));}} return instance;}}// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}
}

飞碟工厂类负责飞碟对象的创建和销毁,在场景中是单实例的,且使用了对象池,实现了缓存功能。

当一个飞碟对象被创建时,会首先在对象池中寻找没有被使用的空闲飞碟对象,有的话就根据规则设置飞碟对象相应属性后直接使用,没有再创建。

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using Unity.VisualScripting.Antlr3.Runtime;
using UnityEngine;
using UnityEngine.Experimental.GlobalIllumination;public class DiskFactory : MonoBehaviour
{public GameObject diskPrefab;private List<GameObject> used;private List<GameObject> free;private int diskIndex;public GameObject GetDisk(Ruler ruler){GameObject disk;// 如果free没有空闲就创建if (free.Count==0){disk = GameObject.Instantiate(diskPrefab, Vector3.zero, Quaternion.identity);disk.name = "UFO" + diskIndex;Debug.LogFormat("factory create a disk, index is {0}",diskIndex);disk.AddComponent(typeof(Disk));diskIndex++;}else{int freeNum = free.Count;disk = free[freeNum-1];free.Remove(free[freeNum-1]);}// cacheused.Add(disk);// 初始化disk featuredisk.GetComponent<Disk>().diskFeature = ruler.diskFeature;// initial disk colorRenderer render = disk.GetComponent<Renderer>();if (ruler.diskFeature.color == DiskFeature.colors.white){render.material.color = Color.red;}else if (ruler.diskFeature.color == DiskFeature.colors.blue){render.material.color = Color.blue;}else if (ruler.diskFeature.color == DiskFeature.colors.black){render.material.color = Color.black;}else if (ruler.diskFeature.color == DiskFeature.colors.yellow){render.material.color = Color.yellow;}else if (ruler.diskFeature.color == DiskFeature.colors.green){render.material.color= Color.green;}// set disk position and scaledisk.transform.localScale = new Vector3(1f, (float)ruler.diskFeature.size, 1f);disk.transform.position = ruler.startPos;disk.SetActive(true);return disk;}public void FreeDisk(GameObject dd){foreach (GameObject d in used){if (d.GetInstanceID()==dd.GetInstanceID()){Debug.LogFormat("factory free a disk, name is {0}", d.name);d.SetActive(false);used.Remove(d);free.Add(d);break;}}}// Start is called before the first frame updatevoid Start(){diskPrefab = GameObject.Instantiate(Resources.Load<GameObject>("Prefabs/UFO"));diskPrefab.SetActive(false);Debug.Log("DiskFactory initial the diskPrefab");used = new List<GameObject>();free = new List<GameObject>();diskIndex = 0;}// Update is called once per framevoid Update(){}
}

model

飞碟的model类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Disk : MonoBehaviour
{public DiskFeature diskFeature;// Start is called before the first frame updatevoid Start(){}// Update is called once per framevoid Update(){}
}

飞碟的特征类,有大小,颜色,速度等属性


public struct DiskFeature
{public enum colors{green,blue,red,purple,black,white,yellow,}public int size;public colors color;public int speed;
}

view

视图类,游戏有三种状态:就绪,游戏中,游戏结束
根据游戏状态不同显示不同的gui界面

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;struct Status
{public int score;public string tip;public int roundNum;public int trialNum;public int roundIndex;public int trialIndex;public PhysiclaType type;public GameState gameState;
}public class UserGUI : MonoBehaviour
{private Status status;private ISceneController currentSceneController;private GUIStyle playInfoStyle;// Start is called before the first frame updatevoid Start(){Init();currentSceneController = Director.GetInstance().CurrentSceneController;// set styleplayInfoStyle = new GUIStyle();playInfoStyle.normal.textColor = Color.black;playInfoStyle.fontSize= 25;}private void Init(){status.score= 0;status.tip = "";status.roundNum = 0;status.trialNum = 0;status.gameState = GameState.Ready;}// Update is called once per framevoid Update(){}private void OnGUI(){// titleGUIStyle titleStyle = new GUIStyle();titleStyle.normal.textColor = Color.red;titleStyle.fontSize = 50;GUI.Label(new Rect(Screen.width / 2 - 80, 10, 60, 100), "Hit UFO", titleStyle);// show user pageShowPage();}public void SetIndex(int roundIndex, int  trailIndex){status.roundIndex = roundIndex;status.trialIndex = trailIndex;}public void SetRTNum(int roundNum, int trailNum){status.roundNum = roundNum;status.trialNum= trailNum;}public void SetGameState(GameState gameState){status.gameState = gameState;}/*set property of status*/public void SetScore(int score){status.score = score;}/*base on game status to show different user view*/private void ShowPage(){switch (status.gameState){case GameState.Ready:ShowHomePage();break;case GameState.Playing:ShowPlayingPage();break;case GameState.GameOver:ShowGameoverPage();break;}}private void ShowGameoverPage(){GUI.Label(new Rect(Screen.width / 2 - 40, 60, 60, 100), "游戏结束!", playInfoStyle);if (GUI.Button(new Rect(420, 200, 100, 60), "重新开始")){FirstController f = (FirstController)currentSceneController;// set game statusf.Restart();}}private void ShowPlayingPage(){GUI.Label(new Rect(10, 10, 60, 100), "正在游戏",playInfoStyle);GUI.Label(new Rect(Screen.width  - 200, 10, 60, 100), "round:" +(status.roundIndex+1)+"  trail:"+ (status.trialIndex+1), playInfoStyle);GUI.Label(new Rect(10, 40, 60, 100), "得分:"+status.score, playInfoStyle);GUI.Label(new Rect(Screen.width - 200, 35, 60, 100),"总轮数:" + status.roundNum + "\n每轮射击数:" + status.trialNum, playInfoStyle);GUI.Label(new Rect(10, 70, 60, 100),"当前模式:"+(status.type==PhysiclaType.noPhysical?"运动学模式":"物理模式"), playInfoStyle);if (Input.GetButtonDown("Fire1")){Singleton<RoundController>.Instance.Hit(Input.mousePosition);}// chose modeif (GUI.Button(new Rect(50, 450, 80, 50), "运动学模式")){FirstController f = (FirstController)currentSceneController;f.SetGameMode(PhysiclaType.noPhysical);}if (GUI.Button(new Rect(150, 450, 80, 50), "物理学模式")){FirstController f = (FirstController)currentSceneController;f.SetGameMode(PhysiclaType.pysical);}}private void ShowHomePage(){// add game modeif (GUI.Button(new Rect(Screen.width/2-50, 100, 100, 60), "开始游戏式\n(默认为运动学模式)")){FirstController f = (FirstController)currentSceneController;// set game statusf.gameState = GameState.Playing;status.gameState = GameState.Playing;}}internal void SetPhyMode(PhysiclaType phyType){status.type= phyType;}
}

创建预制&&配置ruler&&配置天空盒&&挂载脚本

飞碟的预制,需要提前制作好,并加上刚体组件Rigidbody,将Use Gravity项勾选上

在这里插入图片描述

配置游戏对局数据,这样就不用要修改都在代码里修改了

在这里插入图片描述

先创建一个config,之后配置我们的对局数据

在这里插入图片描述
在这里插入图片描述

再给camera配上一个天空盒

在这里插入图片描述

最后挂载firstcontroller和ruler,config脚本到一个空gameobject上

在这里插入图片描述

游戏就可以运行了

视频演示

unity 打飞碟小游戏

代码仓库

GitHub地址

这篇关于unity:打飞碟小游戏的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/631070

相关文章

Unity Post Process Unity后处理学习日志

Unity Post Process Unity后处理学习日志 在现代游戏开发中,后处理(Post Processing)技术已经成为提升游戏画面质量的关键工具。Unity的后处理栈(Post Processing Stack)是一个强大的插件,它允许开发者为游戏场景添加各种视觉效果,如景深、色彩校正、辉光、模糊等。这些效果不仅能够增强游戏的视觉吸引力,还能帮助传达特定的情感和氛围。 文档

Unity协程搭配队列开发Tips弹窗模块

概述 在Unity游戏开发过程中,提示系统是提升用户体验的重要组成部分。一个设计良好的提示窗口不仅能及时传达信息给玩家,还应当做到不干扰游戏流程。本文将探讨如何使用Unity的协程(Coroutine)配合队列(Queue)数据结构来构建一个高效且可扩展的Tips弹窗模块。 技术模块介绍 1. Unity协程(Coroutines) 协程是Unity中的一种特殊函数类型,允许异步操作的实现

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光

Unity 资源 之 Super Confetti FX:点亮项目的璀璨粒子之光 一,前言二,资源包内容三,免费获取资源包 一,前言 在创意的世界里,每一个细节都能决定一个项目的独特魅力。今天,要向大家介绍一款令人惊艳的粒子效果包 ——Super Confetti FX。 二,资源包内容 💥充满活力与动态,是 Super Confetti FX 最显著的标签。它宛如一位

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(4)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)-CSDN博客  这节就是真正的存储数据了   理清一下思路: 1.存储路径并检查 //2进制文件类存储private static string Data_Binary_Pa

Unity Adressables 使用说明(一)概述

使用 Adressables 组织管理 Asset Addressables 包基于 Unity 的 AssetBundles 系统,并提供了一个用户界面来管理您的 AssetBundles。当您使一个资源可寻址(Addressable)时,您可以使用该资源的地址从任何地方加载它。无论资源是在本地应用程序中可用还是存储在远程内容分发网络上,Addressable 系统都会定位并返回该资源。 您

Unity Adressables 使用说明(六)加载(Load) Addressable Assets

【概述】Load Addressable Assets Addressables类提供了加载 Addressable assets 的方法。你可以一次加载一个资源或批量加载资源。为了识别要加载的资源,你需要向加载方法传递一个键或键列表。键可以是以下对象之一: Address:包含你分配给资源的地址的字符串。Label:包含分配给一个或多个资源的标签的字符串。AssetReference Obj

在Unity环境中使用UTF-8编码

为什么要讨论这个问题         为了避免乱码和更好的跨平台         我刚开始开发时是使用VS开发,Unity自身默认使用UTF-8 without BOM格式,但是在Unity中创建一个脚本,使用VS打开,VS自身默认使用GB2312(它应该是对应了你电脑的window版本默认选取了国标编码,或者是因为一些其他的原因)读取脚本,默认是看不到在VS中的编码格式,下面我介绍一种简单快

Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(3)

本文仅作笔记学习和分享,不用做任何商业用途 本文包括但不限于unity官方手册,unity唐老狮等教程知识,如有不足还请斧正​​ Unity数据持久化 之 一个通过2进制读取Excel并存储的轮子(2) (*****生成数据结构类的方式特别有趣****)-CSDN博客 做完了数据结构类,该做一个存储类了,也就是生成一个字典类(只是声明)  实现和上一节的数据结构类的方式大同小异,所

【Unity小技巧】URP管线遮挡高亮效果

前言 在URP渲染管线环境下实现物体遮挡高亮显示效果,效果如下: Unity URP遮挡高亮 实现步骤 创建层级,为需要显示高亮效果的物体添加层级,比如Player 创建一个材质球,也就是高亮效果显示的材质球找到Universal Renderer Data Assets 4.在Assets上添加两个Render Objects组件 第一个做如下三处设置 指定遮挡层级指

【Unity面经】实习篇:面试官常问的一百个面试题

👨‍💻个人主页:@元宇宙-秩沅 👨‍💻 hallo 欢迎 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍💻 本文由 秩沅 原创 👨‍💻 专栏交流🧧🟥Unity100个实战基础✨🎁🟦 Unity100个精华一记✨🎁🟩 Unity50个demo案例教程✨🎁🟨 Unity100个精华细节BUG✨🎁🟨 Unity100个面试题✨🎁 文章