unity小游戏-打飞碟

一、回顾

        上一个作业实现了动作分离+MVC,也就是Model+View+Controller+Action。同时,由于游戏对象自创建开始到游戏结束一直存在,游戏对象的创建、回收仍然由conroller统一管理。当游戏对象在游戏过程中提前被回收,同时又有新的游戏对象产生,对象的管理工作就不得不从controller中剥离出来,否则不但管理困难,游戏对象还难以复用。

        因此,这次在前面的框架的基础上,进一步细化:建立“游戏对象工厂”,统一管理游戏过程中游戏对象的产生、回收、复用等等。

二、游戏介绍

        在打飞碟游戏中,每一轮都会有新的飞碟产生,鼠标点中飞碟则被视为“打中飞碟”,会使飞碟消失,飞碟飞出屏幕区域同样会消失,一轮中飞碟全部消失则进入下一轮。从这里可以看到,游戏对象的创建与销毁是频繁的,而且新对象与旧对象之间没有实质性的差异,是可以复用的。

        视频链接:unity小游戏-打飞碟_单机游戏热门视频

三、实现

        代码实现部分借鉴了unity 实现简易打飞碟游戏-CSDN博客,respect。

        1、

        首先总体框架不变,Model+View+Controller+Action,对象工厂放在controller中(或许也可以提出来)。

         2、Actions

        Actions中的SSActionManager、SSAction几乎不变。

        对应的CCActionManager、CCAction逻辑如下:

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

public class CCActionManager : SSActionManager, IActionCallback 
{
    public RoundController sceneController;//RoundController相当于FirstController
    public CCAction action;
    public DiskFactory factory;
    
    // Start is called before the first frame update
    new void Start()
    {
        sceneController = Singleton<RoundController>.Instance;
        sceneController.actionManager = this;
        factory = Singleton<DiskFactory>.Instance;
    }

    //回调函数
    public void SSActionEvent(SSAction source,
        SSActionEventType events = SSActionEventType.Completed,
        int intParam = 0,
        string strParam = null,
        Object objectParam = null) {
            //
            factory.FreeDisk(source.transform.gameObject);
    }

    public void MoveDisk(GameObject disk) {
        action = CCAction.GetSSAction(disk.GetComponent<DiskAttributes>().speedX, disk.GetComponent<DiskAttributes>().speedY);
        RunAction(disk, action, this);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//飞碟从界面左右两侧飞入,离开界面时运动结束
public class CCAction : SSAction
{
    public float speedX;
    public float speedY;
    public static CCAction GetSSAction(float x, float y) {
        CCAction action = ScriptableObject.CreateInstance<CCAction>();
        //CCFlyAction action = SSAction.Instance;
        action.speedX = x;
        action.speedY = y;
        return action;
    }
    // Start is called before the first frame update
    public override void Start()
    {
        
    }

    // Update is called once per frame
    public override void Update()
    {
        //检查当前脚本所附加的游戏对象是否处于活动状态
        if (this.transform.gameObject.activeSelf == false) {//飞碟已经被"销毁"
            Debug.Log("1");
            this.destroy = true;
            this.callback.SSActionEvent(this);
            return;
        }
        Vector3 vec3 = Camera.main.WorldToScreenPoint (this.transform.position);
        if (vec3.x < -200 || vec3.x > Camera.main.pixelWidth + 200 || vec3.y < -200 || vec3.y > Camera.main.pixelHeight + 200) {
            Debug.Log("2");
            this.destroy = true;
            this.callback.SSActionEvent(this);
            return;
        }
        //更新位置
        //Time.deltaTime表示从上一帧到当前帧的时间间隔
        // += 速度*时间
        transform.position += new Vector3(speedX, speedY, 0) * Time.deltaTime * 2;
        
        
    }
}
       3、Controller

        RoundController负责管理轮次、调用对象工厂生成对象、检测鼠标是否点中飞碟,同时实现FirstController(场记)的功能。

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

public class RoundController : MonoBehaviour, ISceneController, IUserAction
{
    int round = 0;
    int max_round = 5;
    float timer = 0.5f;
    GameObject disk;
    DiskFactory factory ;
    public CCActionManager actionManager;
    public ScoreController scoreController;
    public UserGUI userGUI;

    //before Start
    void Awake() {
        SSDirector director = SSDirector.getInstance();
        director.currentSceneController = this;
        director.currentSceneController.LoadSource();
        
        //Add同时赋值给RoundController的CCAcM、ScoreC
        gameObject.AddComponent<CCActionManager>();
        gameObject.AddComponent<ScoreController>();

        gameObject.AddComponent<DiskFactory>();
        factory = Singleton<DiskFactory>.Instance;
        gameObject.AddComponent<UserGUI>();
        userGUI = gameObject.GetComponent<UserGUI>();
        
    }
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (userGUI.mode == 0) return;
        GetHit();
        gameOver();
        if (round > max_round) {
            return;
        }
        timer -= Time.deltaTime;
        if (timer <= 0 && actionManager.RemainActionCount() == 0) {
            //从工厂中得到10个飞碟,为其加上动作
            for (int i = 0; i < 10; ++i) {
                disk = factory.GetDisk(round);
                actionManager.MoveDisk(disk);
            }
            round += 1;
            if (round <= max_round) {
                userGUI.round = round;
            }
            timer = 4.0f;
        }
        
    }

    public void LoadSource() 
    {

    }

    public void gameOver() 
    {
        if (round > max_round && actionManager.RemainActionCount() == 0)
            userGUI.gameMessage = "THE END";
    }

    public void GetHit() {
        if (Input.GetButtonDown("Fire1")) {

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

			//create ray, origin is camera, and direction to mousepoint
			Camera ca = Camera.main;
			Ray ray = ca.ScreenPointToRay(Input.mousePosition);

			//Return the ray's hit
			RaycastHit hit;
			if (Physics.Raycast(ray, out hit)) {
                //点中飞碟则加分
                scoreController.Record(hit.transform.gameObject);
                //设为不可见
                hit.transform.gameObject.SetActive(false);
			}
            else{
                //没点中飞碟则惩罚
                UnityEngine.Debug.Log("nope");
                scoreController.Publish();
            }
		}
    }
}

        ScoreController负责记分,包括增加飞碟对应的分数、惩罚减少分数。它在RoundController中被调用。

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

public class ScoreController : MonoBehaviour
{
    int score;
    public RoundController roundController;
    public UserGUI userGUI;
    // Start is called before the first frame update
    void Start()
    {
        roundController = (RoundController)SSDirector.getInstance().currentSceneController;

        //这行代码使得RoundController中的gameObject.AddComponent<ScoreController>();的同时,
        //将自身赋给了roundController.scoreController 
        roundController.scoreController = this;

        userGUI = this.gameObject.GetComponent<UserGUI>();
    }
    //加分
    public void Record(GameObject disk) {
        score += disk.GetComponent<DiskAttributes>().score;
        userGUI.score = score;
    }
    //扣分
    public void Publish(){
        UnityEngine.Debug.Log("publish");
        if(score>=1)
            score-=1;
        userGUI.score = score;
    }

    
}

        DiskFactory:

        功能:生产飞碟、设置飞碟属性值、管理飞碟。通过两个队列:free、used,分别管理空闲飞碟、正在被使用的飞碟。当需要新的飞碟的时候,就从空闲飞碟队列中取出一个返回,并且放到正在被使用飞碟中管理。当一个飞碟被销毁(实际上没有销毁),就从正在被使用队列移动到空闲飞碟,实现复用。

using System.IO;
using System.Diagnostics;
using System.Security.AccessControl;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyException : System.Exception
{
    public MyException() { }
    public MyException(string message) : base(message) { }
}

public class DiskFactory : MonoBehaviour
{
    
    List<GameObject> disk_used;
    List<GameObject> disk_free;
    System.Random rand;
    //public int miss=0;
    // Start is called before the first frame update
    void Start()
    {
        
        disk_used = new List<GameObject>();
        disk_free = new List<GameObject>();
        rand = new System.Random();
        //Disk disk = GetDisk(1); 
    }

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

    void setAttributes(GameObject disk,int round){
        //根据不同round设置diskAttributes的值
        //随意的旋转角度
        disk.transform.localEulerAngles = new Vector3(-rand.Next(20,40),0,0);

        DiskAttributes attri = disk.GetComponent<DiskAttributes>();
        attri.score = rand.Next(1,4);
        //由分数来决定速度、颜色、大小
        //速度(大小)
        attri.speedX = (rand.Next(1,5) + attri.score + round) * 0.2f;
        attri.speedY = (rand.Next(1,5) + attri.score + round) * 0.2f;
        //颜色
        Color[] color={Color.blue, Color.green, Color.red};
        disk.GetComponent<Renderer>().material.color = color[attri.score-1];
        //大小
        disk.transform.localScale += new Vector3(-0.2f * (attri.score-1), 0, -0.2f * (attri.score-1));
        
        //飞碟可从四个方向飞入(左上、左下、右上、右下)
        int[,] dir={{1,1},{1,-1},{-1,1},{-1,-1}};
        float[,] screenPos={{Screen.width*0.1f,0},{Screen.width*0.1f,Screen.height*1.1f},{Screen.width*1f,0},{Screen.width*0.8f,Screen.height*0.8f}};
        int direction = rand.Next(0,4);

        //速度方向
        attri.speedX *= dir[direction,0];
        attri.speedY *= dir[direction,1];
        //初始位置
        disk.transform.Translate(Camera.main.ScreenToWorldPoint(new Vector3(screenPos[direction,0], screenPos[direction,1], 8)));
        UnityEngine.Debug.Log("trans",disk.transform);
    }

    public GameObject GetDisk(int round) {
        GameObject disk;
        if (disk_free.Count != 0) {
            disk = disk_free[0];
            disk_free.Remove(disk);
        }
        else {
            disk=new Disk().disk;
        }
        setAttributes(disk,round);
        //setAttributes(disk,round);
        disk_used.Add(disk);
        disk.SetActive(true);
        UnityEngine.Debug.Log("generate disk");
        return disk;
    }

    public void FreeDisk(GameObject disk) {
        disk.SetActive(false);
        //将位置和大小恢复到预制,这点很重要!
        disk.transform.position = new Vector3(0, 0,0);
        disk.transform.localScale = new Vector3(2f,0.1f,2f);
        if (!disk_used.Contains(disk)) {
            throw new MyException("Try to remove a item from a list which doesn't contain it.");
        }
        UnityEngine.Debug.Log("free disk");
        disk_used.Remove(disk);
        disk_free.Add(disk);
    }
}

        Singleton:这个类并非控制器,它实现了单示例接口,保证游戏中一些实例对象的单一(比如只有一个对象工厂、记分员ScoreController、场记RoundController等等)

使用:在前面Action、Controller都有使用,比如CCActionManager中的:

        sceneController = Singleton<RoundController>.Instance;
        factory = Singleton<DiskFactory>.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;  
		}  
	}
}
4、Model、Views

        飞碟Disk

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

public class DiskAttributes : MonoBehaviour
{
    //public GameObject gameobj;
    public int score;
    public float speedX;
    public float speedY;
}

public class Disk
{
    public GameObject disk;


    public Disk(){
        disk = GameObject.Instantiate(Resources.Load("Prefabs/disk", typeof(GameObject))) as GameObject;
        disk.AddComponent<DiskAttributes>();
    }
}

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

public class UserGUI : MonoBehaviour
{
    public int mode;
    public int score;
    public int miss;
    public int round;
    public string gameMessage;
    private IUserAction action;
    public GUIStyle bigStyle, blackStyle, smallStyle;//自定义字体格式
    public Font pixelFont;
    private int menu_width = Screen.width / 5, menu_height = Screen.width / 10;//主菜单每一个按键的宽度和高度
    // Start is called before the first frame update
    void Start()
    {
        mode = 0;
        score = 0;
        miss = 0;
        round = 0;
        gameMessage = "";
        action = SSDirector.getInstance().currentSceneController as IUserAction;

        //pixelStyle
        //pixelFont = Font.Instantiate(Resources.Load("Fonts/ThaleahFat", typeof(Font))) as Font;
        //if (pixelFont == null) Debug.Log("null");
        //pixelFont.fontSize = 50;
        //pixelFont = Arial;
        
        //大字体初始化
        bigStyle = new GUIStyle();
        bigStyle.normal.textColor = Color.white;
        bigStyle.normal.background = null;
        bigStyle.fontSize = 50;
        bigStyle.alignment=TextAnchor.MiddleCenter;

        //black
        blackStyle = new GUIStyle();
        blackStyle.normal.textColor = Color.black;
        blackStyle.normal.background = null;
        blackStyle.fontSize = 50;
        blackStyle.alignment=TextAnchor.MiddleCenter;

        //小字体初始化
        smallStyle = new GUIStyle();
        smallStyle.normal.textColor = Color.white;
        smallStyle.normal.background = null;
        smallStyle.fontSize = 20;
        smallStyle.alignment=TextAnchor.MiddleCenter;

    }

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

    void OnGUI() {
        //GUI.skin.button.font = pixelFont;
        GUI.skin.button.fontSize = 35;
        switch(mode) {
            case 0:
                mainMenu();
                break;
            case 1:
                GameStart();
                break;
            
        }       
    }

    void mainMenu() {
        GUI.Label(new Rect(Screen.width / 2 - menu_width * 0.5f, Screen.height * 0.1f, menu_width, menu_height), "HIT UFO", bigStyle);
        bool button = GUI.Button(new Rect(Screen.width / 2 - menu_width * 0.5f, Screen.height * 3 / 7, menu_width, menu_height), "START");
        if (button) {
            mode = 1;
        }
        
    }
    void GameStart() {
        UnityEngine.Debug.Log(Camera.main.pixelWidth);
        UnityEngine.Debug.Log(Screen.width*1f);
        GUI.Label(new Rect(Screen.width / 2 - menu_width * 0.5f, Screen.height * 0.1f, menu_width, menu_height), gameMessage,bigStyle);//, bigStyle300, 60, 50, 200
        GUI.Label(new Rect(Screen.width*0.8f, Screen.height * 0.8f-30, menu_width, menu_height), "Score: " + score, smallStyle);//0,0,100,50
        GUI.Label(new Rect(Screen.width*0.8f, Screen.height * 0.8f, menu_width, menu_height), "Round: " + round, smallStyle);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
该资源内项目源码是个人的课程设计、毕业设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 该资源内项目源码是个人的课程设计,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到96分,放心下载使用! ## 项目备注 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值