Game-unity hw5

Game-unity hw5

作业要求

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

  • 游戏内容要求:
    1. 游戏有 n 个 round,每个 round 都包括10 次 trial;
    2. 每个 trial 的飞碟的色彩、大小、发射位置、速度、角度、同时出现的个数都可能不同。它们由该 round 的 ruler 控制;
    3. 每个 trial 的飞碟有随机性,总体难度随 round 上升;
    4. 鼠标点中得分,得分规则按色彩、大小、速度不同计算,规则可自由设定。
  • 游戏的要求:
    • 使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类
    • 近可能使用前面 MVC 结构实现人机交互与游戏模型分离

游戏设计

​ 游戏一共5轮,每一轮生成10个飞碟,难度随着轮次增加。

​ 一共有三种颜色的飞碟,飞行速度从慢到快:黄色、红色、黑色,击中得到的分数分别是10,20,40。

​ 第一轮只有黄色;第二轮有红色和黄色,红色出现的概率是25%;第三轮也是有红色和黄色,红色出现 概率是50%;第四轮有黄色、红色和黑色,红色出现概率是3/8,黑色出现的概率是25%;第五轮也是有 三种颜色,红色出现的概率30%,黑色出现的概率40%。

​ 五轮结束后展示总共得分。

代码设计

动作管理

CCFlyAction.cs

该脚本主要实现飞碟的飞行动作:抛物线运动。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCFlyAction : SSAction
{

    /**
     * acceleration是重力加速度,为9.8
     */

    float acceleration;

    /**
     * horizontalSpeed是飞碟水平方向的速度
     */

    float horizontalSpeed;

    /**
     * direction是飞碟的初始飞行方向
     */

    Vector3 direction;

    /**
     * time是飞碟已经飞行的时间
     */

    float time;

    public override void Start()
    {
        enable = true;
        acceleration = 9.8f;
        time = 0;
        horizontalSpeed = gameobject.GetComponent<DiskObj>().speed;
        direction = gameobject.GetComponent<DiskObj>().direction;
    }

    // Update is called once per frame
    public override void Update()
    {
        if (gameobject.activeSelf)
        {
            /**
             * 计算飞碟的累计飞行时间
             */
            time += Time.deltaTime;

            /**
             * 飞碟在竖直方向的运动
             */

            transform.Translate(Vector3.down * acceleration * time * Time.deltaTime);

            /**
             * 飞碟在水平方向的运动
             */

            transform.Translate(direction * horizontalSpeed * Time.deltaTime);

            /**
             * 当飞碟的y坐标比-4小时,飞碟落地
             */

            if (this.transform.position.y < -4)
            {
                this.destroy = true;
                this.enable = false;
                this.callback.SSActionEvent(this);
            }
        }

    }

    public static CCFlyAction GetSSAction()
    {
        CCFlyAction action = ScriptableObject.CreateInstance<CCFlyAction>();
        return action;
    }
}

CCActionManager.cs

该脚本是动作管理器,每一轮把10个飞碟的飞行动作储存起来(体现在StartThrow函数)。

![1](D:\课件\3D游戏\5\img\1.png)using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CCActionManager : SSActionManager, ISSActionCallback
{

    public FirstSceneControl sceneController;
    public List<CCFlyAction> Fly;
    public int DiskNumber = 0;

    /**
     * used是用来保存正在使用的动作
     * free是用来保存还未被激活的动作
     */

    private List<SSAction> used = new List<SSAction>();
    private List<SSAction> free = new List<SSAction>();

    /**
     * GetSSAction这个函数是用来获取CCFlyAction这个动作的,
     * 每次首次判断free那里还有没有未使用的CCFlyActon这个动作,
     * 有就从free那里获取,没有就生成一个CCFlyAction
     */

    SSAction GetSSAction()
    {
        SSAction action = null;
        if (free.Count > 0)
        {
            action = free[0];
            free.Remove(free[0]);
        }
        else
        {
            action = ScriptableObject.Instantiate<CCFlyAction>(Fly[0]);
        }

        used.Add(action);
        return action;
    }

    public void FreeSSAction(SSAction action)
    {
        SSAction tmp = null;
        foreach (SSAction i in used)
        {
            if (action.GetInstanceID() == i.GetInstanceID())
            {
                tmp = i;
            }
        }
        if (tmp != null)
        {
            tmp.reset();
            free.Add(tmp);
            used.Remove(tmp);
        }
    }

    protected void Start()
    {   Debug.Log("Start");
        sceneController = (FirstSceneControl)Director.getInstance().currentSceneControl;
        sceneController.actionManager = this;
        Fly.Add(CCFlyAction.GetSSAction());

    }

    public void SSActionEvent(SSAction source,
        SSActionEventType events = SSActionEventType.Competeted,
        int intParam = 0,
        string strParam = null,
        UnityEngine.Object objectParam = null)
    {
        if (source is CCFlyAction)
        {
            DiskNumber--;
            DiskFactory df = Singleton<DiskFactory>.Instance;
            df.FreeDisk(source.gameobject);
            FreeSSAction(source);
        }
    }

    public void StartThrow(Queue<GameObject> diskQueue)
    {
        foreach (GameObject tmp in diskQueue)
        {   
            //RunAction用于初始化动作。
            RunAction(tmp, GetSSAction(), (ISSActionCallback)this);
        }
    }
}

对象管理

首先创建一个扁平圆柱体作为飞碟的预制。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aNwD0SZ2-1603811973266)(./img/1.png)]

飞碟的游戏对象为DiskObj,其声明如下:

public class DiskObj : MonoBehaviour {

    public Vector3 size;
    public Color color;
    public float speed;
    public Vector3 direction;
}

DiskFactory.cs

DiskFactory为一个带缓存的工厂,用于生产和回收飞碟。

分别声明一个名为used和一个名为free的Diskobj游戏对象序列,used储存即将飞出的飞碟,free储存已被击中或未被击中而掉落地面的飞碟。

以生成飞碟的函数GetDisk为例,如果free序列有空闲飞碟,则直接将其放入待用飞碟序列,若无,则根据轮次生成不同颜色的飞碟并加入到used序列。

public GameObject GetDisk(int round)
	{
		GameObject newDisk = null;
		//若有闲置的飞碟,则将其放入待用飞碟序列
		if (free.Count > 0)
		{
			newDisk = free[0].gameObject;
			free.Remove(free[0]);
		}
		else
		{
			newDisk = GameObject.Instantiate<GameObject>(diskPrefab, Vector3.zero, Quaternion.identity);
			newDisk.AddComponent<DiskObj>();
		}
		//根据轮数生成飞碟
		int selectedColor = Random.Range(0, round * 200);

		if (selectedColor > 600)
		{
			round = 2;
		}
		else if (selectedColor > 300)
		{
			round = 1;
		}
		else
		{
			round = 0;
		}

		switch (round)
		{

		case 0:
			{
				newDisk.GetComponent<DiskObj>().color = Color.yellow;
				newDisk.GetComponent<DiskObj>().speed = 4.0f;
				float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;
				newDisk.GetComponent<DiskObj>().direction = new Vector3(RanX, 1, 0);
				newDisk.GetComponent<Renderer>().material.color = Color.yellow;
				break;
			}
		case 1:
			{
				newDisk.GetComponent<DiskObj>().color = Color.red;
				newDisk.GetComponent<DiskObj>().speed = 6.0f;
				float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;
				newDisk.GetComponent<DiskObj>().direction = new Vector3(RanX, 1, 0);
				newDisk.GetComponent<Renderer>().material.color = Color.red;
				break;
			}
		case 2:
			{
				newDisk.GetComponent<DiskObj>().color = Color.black;
				newDisk.GetComponent<DiskObj>().speed = 7.0f;
				float RanX = UnityEngine.Random.Range(-1f, 1f) < 0 ? -1 : 1;
				newDisk.GetComponent<DiskObj>().direction = new Vector3(RanX, 1, 0);
				newDisk.GetComponent<Renderer>().material.color = Color.black;
				break;
			}
		}

		used.Add(newDisk.GetComponent<DiskObj>());

		newDisk.name = newDisk.GetInstanceID().ToString();
		return newDisk;
	}

Singleton.cs

由于按照要求,工厂必须是场景单实例的,所以需要一个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;
        }
    }
}

场景管理

FirstSceneControl.cs

本游戏只有一个场景,因此只需要要一个场景管理器。

场景管理器需要记录当前游戏的状态(一轮开始、一轮结束、一轮正在进行、游戏暂停、游戏开始、游戏结束)。每当一轮开始,通知动作管理器存入十个飞碟的飞出动作。每过1秒飞出一个飞碟。并且设置以鼠标点击位置发出射线,与飞碟碰撞则表明击中飞碟。

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public interface ISceneControl
{

    void LoadResources();
}

public class FirstSceneControl : MonoBehaviour ,ISceneControl , IUserAction
{

    public CCActionManager actionManager { get; set; }
    public ScoreRecorder scoreRecorder { get; set; }
    public Queue<GameObject> diskQueue = new Queue<GameObject>();

    private int diskNumber;
    private int currentRound = 0;
    public int round;
    private float time = 0;
    private GameState gameState = GameState.START;

    void Awake()
    {
        Director director = Director.getInstance();
        director.currentSceneControl = this;
        diskNumber = 1;
        round = 5;
        this.gameObject.AddComponent<ScoreRecorder>();
        this.gameObject.AddComponent<DiskFactory>();
        scoreRecorder = Singleton<ScoreRecorder>.Instance;
        //director.currentSceneControl.LoadResources();
    }

    private void Update()
    {
        if (actionManager.DiskNumber == 0 && gameState == GameState.RUNNING)
        {
            gameState = GameState.ROUND_FINISH;
            if(currentRound==5){
                gameState = GameState.END;
            }
        }

        if (actionManager.DiskNumber == 0 && gameState == GameState.ROUND_START)
        {
            currentRound = currentRound + 1;
            NextRound();
            actionManager.DiskNumber = 1;
            gameState = GameState.RUNNING;
        }

        if (time > 1)
        {
            ThrowDisk();
            time = 0;
        }
        else
        {
            time += Time.deltaTime;
        }


    }

    private void NextRound()
    {
        DiskFactory df = Singleton<DiskFactory>.Instance;
        for (int i = 0; i < diskNumber; i++)
        {
            diskQueue.Enqueue(df.GetDisk(currentRound));
        }

        actionManager.StartThrow(diskQueue);

    }

    void ThrowDisk()
    {
        if (diskQueue.Count != 0)
        {
            GameObject disk = diskQueue.Dequeue();

            /**
             * 以下几句代码是随机确定飞碟出现的位置
             */

            Vector3 position = new Vector3(0, 0, 0);
            float y = UnityEngine.Random.Range(0f, 3f);
            position = new Vector3(-disk.GetComponent<DiskObj>().direction.x * 7, y, 0);
            disk.transform.position = position;

            disk.SetActive(true);
        }

    }

    public void LoadResources()
    {
        //DiskFactory df = Singleton<DiskFactory>.Instance;
        //df.init(diskNumber);
        //GameObject greensward = GameObject.Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/greensward"));
    }


    public void GameOver()
    {
        GUI.color = Color.red;
        GUI.Label(new Rect(700, 300, 400, 400), "GAMEOVER");

    }

    public int GetRound()
    {
        return currentRound;
    }

    public int GetScore()
    {
        return scoreRecorder.score;
    }

    public GameState getGameState()
    {
        return gameState;
    }

    public void setGameState(GameState gs)
    {
        gameState = gs;
    }

    public void hit(Vector3 pos)
    {
        Ray ray = Camera.main.ScreenPointToRay(pos);

        RaycastHit[] hits;
        hits = Physics.RaycastAll(ray);
        for (int i = 0; i < hits.Length; i++)
        {
            RaycastHit hit = hits[i];

            if (hit.collider.gameObject.GetComponent<DiskObj>() != null)
            {
                scoreRecorder.Record(hit.collider.gameObject);

                /**
                 * 如果飞碟被击中,那么就移到地面之下,由工厂负责回收
                 */

                hit.collider.gameObject.transform.position = new Vector3(0, -5, 0);
            }

        }
    }
}

交互管理

UserGUI.cs

用于用户交互,游戏一开始需要玩家点击Start按钮开始游戏,每一轮需要点击Next Round进入下一轮,在游戏进行时显示实时得分和轮数,当游戏结束显示得分。

这里只放出OnGUI函数

    private void OnGUI()
    {
        if (Input.GetButtonDown("Fire1"))
        {

            Vector3 pos = Input.mousePosition;
            action.hit(pos);

        }

        GUIStyle myStyle = new GUIStyle();
        myStyle.fontSize = 20;

        GUI.Label(new Rect(5, 5, 200, 200), "Score:" + action.GetScore().ToString(), myStyle);
        GUI.Label(new Rect(5, 50, 200, 200), "Round:" + action.GetRound().ToString(), myStyle);

        if (isFirst && GUI.Button(new Rect(360, 150, 90, 50), "Start"))
        {
            isFirst = false;
            action.setGameState(GameState.ROUND_START);
     
        }

        if (!isFirst && action.getGameState() == GameState.ROUND_FINISH && GUI.Button(new Rect(350, 150, 120, 60), "Next Round"))
        {
            action.setGameState(GameState.ROUND_START);
          
        }

        if (action.getGameState()==GameState.END)
        {
            myStyle.fontSize = 40;
             GUI.Label(new Rect(220, 150, 400, 200), "GameOver!Score:" + action.GetScore().ToString(), myStyle);
        }

    }

得分管理

ScoreRecorder.cs

用于计分的脚本。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScoreRecorder : MonoBehaviour
{

    /**
     * score是玩家得到的总分
     */

    public int score;

    /**
     * scoreTable是一个得分的规则表,每种飞碟的颜色对应着一个分数
     */

    private Dictionary<Color, int> scoreTable = new Dictionary<Color, int>();

    // Use this for initialization
    void Start()
    {
        score = 0;
        scoreTable.Add(Color.yellow, 10);
        scoreTable.Add(Color.red, 20);
        scoreTable.Add(Color.black, 40);
    }

    public void Record(GameObject disk)
    {
        score += scoreTable[disk.GetComponent<DiskObj>().color];
    }

    public void Reset()
    {
        score = 0;
    }
}

游戏运行结果

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值