05-与游戏世界交互

这篇博客介绍了如何在Unity中编写一个鼠标打飞碟的游戏,涉及游戏规则、飞碟的随机性和难度设置,以及如何使用工厂模式、MVC结构进行代码组织。此外,还提到了一个选做任务——编写自定义Component来实现特殊效果。
摘要由CSDN通过智能技术生成

Homework05

项目地址
演示视频
使用说明:创建一个空GameObject将FirstSceneController.cs挂载到新建游戏对象即可

1. 编写一个简单的鼠标打飞碟(Hit UFO)游戏

游戏内容要求:

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

游戏的要求:

  • 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
  • 尽可能使用前面 MVC 结构实现人机交互与游戏模型分离

设计实现如下:

1. 制作UFO prefabs如下
在这里插入图片描述
在这里插入图片描述
2. BaseCode.cs定义UFO类,并定义SSDriector

UFO命名空间下定义,方便其他class调用

namespace UFO {
	public class Disk : MonoBehaviour{
	    public float size;  //大小
	    public Color color; //颜色
	    public float speed; //速度
	    public Vector3 position;  //初始位置
	    public Vector3 direction;  //运动方向
	}
	
	public class Director : System.Object {
		private static Director _instance;
		public ISceneController currentSceneController { get; set; }
		public static Director getInstance() {
			if (_instance == null) {
				_instance = new Director();
			}
			return _instance;
		}
	}

	public interface ISceneController {		//加载场景
		void Init ();
		SceneController  getSceneController();
		//initQueue(mode)
	}
}

3. 实现DiskFactory.cs以工厂模式管理飞碟的生产与回收

  • UFO根据回合数设定不同速度和大小
  • UFO设定随机初始位置和初始速度方向
  • UFO6种颜色随机(白、黄、红、蓝、绿、黑)
public class DiskFactory : MonoBehaviour
{
    private List<Disk> toDelete = new List<Disk>();
    private List<Disk> toUse = new List<Disk>();
    public Color[] colors = {Color.white, Color.yellow, Color.red, Color.blue, Color.green, Color.black};//可选颜色
    public GameObject GetDisk(int round) {  //根据回合数对飞碟设置属性并返回
        GameObject newDisk = null;
        if (toUse.Count > 0) {
            newDisk = toUse[0].gameObject;
            toUse.Remove(toUse[0]);
        } else {
            newDisk = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/UFO"), Vector3.zero, Quaternion.identity);
            newDisk.AddComponent<Disk>();
        }
        // 飞碟的速度为 round * 7
        newDisk.GetComponent<Disk>().speed = 7.0f * round;
        // 飞碟随 round 越来越小
        newDisk.GetComponent<Disk>().size = (1 - round*0.1f);
        // 飞碟颜色随机
        int color = UnityEngine.Random.Range(0, 6);//共有六种颜色
        newDisk.GetComponent<Disk>().color = colors[color];

        // 飞碟的发射方向
        float RanX = UnityEngine.Random.Range(-1, 3) < 1 ? -1 : 1;//-1,0则为负方向,1,2则为正方向
        newDisk.GetComponent<Disk>().direction = new Vector3(-RanX, UnityEngine.Random.Range(-2f, 2f), 0);
        // 飞碟的初始位置
        newDisk.GetComponent<Disk>().position = new Vector3(RanX*13, UnityEngine.Random.Range(-2f, 2f), UnityEngine.Random.Range(-1f, 1f));

        toDelete.Add(newDisk.GetComponent<Disk>());
        newDisk.SetActive(false);
        newDisk.name = newDisk.GetInstanceID().ToString();
        return newDisk;
    }

    public void FreeDisk(GameObject disk){
        Disk cycledDisk = null;
        foreach (Disk toCycle in toDelete){
            if (disk.GetInstanceID() == toCycle.gameObject.GetInstanceID()){
                cycledDisk = toCycle;
            }
        }
        if (cycledDisk != null){
            cycledDisk.gameObject.SetActive(false);
            toUse.Add(cycledDisk);
            toDelete.Remove(cycledDisk);
        }
    }
}

3. 设计ActionManager.cs管理场景的动作

  • 管理飞碟的运动以及与用户交互
  • 点击飞碟,分数加一,飞碟消失
public class ActionManager : MonoBehaviour
{

    public Vector3 direction;//运动方向
    public float speed;//初速度
    public GameObject cam;

    public void diskFly(Vector3 direction,float speed) {//赋予飞碟初速度和方向
        this.direction = direction;
        this.speed = speed;
    }

    // Start is called before the first frame update
    void Start(){
        cam = GameObject.Find("Main Camera");
    }

    // Update is called once per frame
    void Update(){
        
        this.gameObject.transform.position += speed * direction * Time.deltaTime;

        if (Input.GetButtonDown("Fire1")){   //光标拾取物体的结果,即鼠标集中飞碟的结果

            Debug.Log("Fired Pressed");
            Debug.Log(Input.mousePosition);

            Vector3 mp = Input.mousePosition; //get Screen Position

            //create ray, origin is camera, and direction to mousepoint
            Camera ca;
            if (cam != null) ca = cam.GetComponent<Camera>();
            else ca = Camera.main;

            Ray ray = ca.ScreenPointToRay(Input.mousePosition);

            //Return the ray's hits
            RaycastHit[] hits = Physics.RaycastAll(ray);

            foreach (RaycastHit hit in hits){
                print(hit.transform.gameObject.name);
                if (hit.collider.gameObject.tag.Contains("Finish")){ //plane tag
                    Debug.Log("hit " + hit.collider.gameObject.name + "!");
                }
                Singleton<DiskFactory>.Instance.FreeDisk(hit.transform.gameObject);//飞碟消失
                //this.gameObject.GetComponent<UserGUI>().score ++;
                //SceneController.getInstance().addScore();
                Director.getInstance ().currentSceneController.getSceneController().addScore();//分数加一
            }
        }

    }
}

4. 设计SceneController.cs场景控制器

  • 显示信息:回合数,总飞碟数,所得分数
public class SceneController
{
    public int round ;  //回合数
    public int total ;  //总飞碟数
    public int score ;//得到的分数

    private static SceneController sceneCtrl;

    public SceneController() {//用于SceneController归0
        score = 0;
        total = 0;
        score = 0;
    }
    public static SceneController getInstance(){
        if (sceneCtrl == null){
            sceneCtrl = new SceneController();
        }
        return sceneCtrl;
    }

    public void addRound(){
        round++;
    }
    public void addTotal(){
        total++;
    }
    public void addScore(){
        score++;
    }
    
    public int getRound(){
        return round;
    }
    public int getTotal() {
        return total;
    }
    public int getScore() {
        return score;
    }
}

5. 设计FirstSceneController.cs第一场景控制器

  • 控制已发射的UFO个数,10/round
  • 控制飞碟发射时间间隔(与回合数成反比)
  • 负责控制游戏场景的加载和切换
public class FirstSceneController : MonoBehaviour, ISceneController{
    public int diskFlyTimes; //已经发射的飞碟个数,每回合10个,最多30个
    public float time;// 时间,用于控制飞碟发射间隔
    public int round;  // 当前回合数
    // 飞碟队列
    public Queue<GameObject> diskQueue = new Queue<GameObject>();//飞碟队列
    public SceneController  sceneCtrl;

    // Start is called before the first frame update
    void Start() {
        // 当前场景控制器
        Director.getInstance().currentSceneController = this;
        this.gameObject.AddComponent<DiskFactory>();
        this.gameObject.AddComponent<UserGUI>();
        Director.getInstance().currentSceneController.Init();//初始化FirstSceneController相关数据
    }

    // 初始化每个回合的飞碟队列,每个回合的飞碟属性不同
    void initQueue() {
        for(int i = 0; i < 10; i++)
            diskQueue.Enqueue(Singleton<DiskFactory>.Instance.GetDisk(round));
    }

    // Update is called once per frame
    void Update() {
       // round = sceneCtrl.getRound();
        time += Time.deltaTime;
        // 发射飞碟的间隔回合数成反比
        if(time >= 2.0f-0.3*round) {
            if(diskFlyTimes >= 30) {                //游戏结束
                Reset();
            } else if ((diskFlyTimes % 10) == 0 ) { //更新回合(此步骤必须在发射飞碟前面)
                round++;                            //在initQueue()之前
                sceneCtrl.addRound();               //回合数增加
                initQueue();                        //初始化新的飞盘队列
            }

            if (diskFlyTimes < 30) {
                time = 0;
                ThrowDisk();                        //发射飞盘
                diskFlyTimes++;                     //飞盘数增加
                sceneCtrl.addTotal();               //综费盘数增加
            }
        }
    }

    public void ThrowDisk() {
        if(diskQueue.Count > 0) {
            GameObject disk = diskQueue.Dequeue();
            disk.GetComponent<Renderer>().material.color = disk.GetComponent<Disk>().color;
            disk.transform.position = disk.GetComponent<Disk>().position;
            disk.transform.localScale = disk.GetComponent<Disk>().size * disk.transform.localScale;
            disk.SetActive(true);
            disk.AddComponent<ActionManager>();
            disk.GetComponent<ActionManager>().diskFly(disk.GetComponent<Disk>().direction, disk.GetComponent<Disk>().speed);
        }
    }
    public void Init() {
        sceneCtrl = new SceneController();          //SceneController元素归0
        diskFlyTimes = 0;
        time = 0;
        round = 0;
        diskQueue.Clear();                          //清空飞盘队列
    }
    public SceneController  getSceneController() {  //返回SceneController
        return sceneCtrl;
    }
    void Reset() {                                  //游戏重置
        this.gameObject.GetComponent<UserGUI>().reset = 1;
    }

}

7. 设计UserGUI.cs负责显示信息

  • 回合数Round,未击中飞碟数Miss,分数Score
  • 重新开始按钮
  • 游戏界面
public class UserGUI : MonoBehaviour
{
    public int reset;
    GUIStyle style;
	GUIStyle buttonStyle;
    // Start is called before the first frame update
    void Start()
    {
        reset = 0;
        style = new GUIStyle();
		style.fontSize = 30;
		style.normal.textColor = Color.green;// 

		buttonStyle = new GUIStyle("button");
		buttonStyle.fontSize = 30;
		buttonStyle.normal.textColor = Color.green;// 
    }

    // Update is called once per frame
    void Update()
    {

    }

    private void OnGUI() {
        if(reset == 1) {
            if(GUI.Button(new Rect(380, 250, 100, 80), "Reset", buttonStyle)) {
                Director.getInstance().currentSceneController.Init();
                reset = 0;
            }
        }

        int round = Director.getInstance().currentSceneController.getSceneController().getRound();
        int total = Director.getInstance().currentSceneController.getSceneController().getTotal();
        int score = Director.getInstance().currentSceneController.getSceneController().getScore();
        int miss = total - score;//未击中的飞碟数
        string text = "Round: " + round.ToString() + "\nMiss:  " + miss.ToString() + "\nScores:  " + score.ToString();
        GUI.Label(new Rect(10, 10, Screen.width, 50),text,style);      
    }

}
2. 编写一个简单的自定义 Component (选做)

用自定义组件定义几种飞碟,做成预制

实现旋转UFO、透明UFO
在这里插入图片描述

public class Rotate: MonoBehaviour
{
    public float speed;
    public float xangle, yangle, zangle;
    void Update()
    {
        Vector3 axis = new Vector3(xangle, yangle, zangle);
        this.transform.RotateAround(new Vector3(0, 0, 0), axis, speed * Time.deltaTime);
        this.transform.Rotate(Vector3.up * 100 * Time.deltaTime);
    }
}

参考博客
师兄博客
师兄博客2

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值