3D游戏编程与设计5——与游戏世界交互

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

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

在这里插入图片描述

代码说明:
  • 飞碟回收工厂类 DiskFactory.cs:
    这个类主要是用来实现飞碟的资源管理,如生成飞碟、自动回收飞碟等,使用 List 数组存储飞碟对象,方便管理。具体实现如下:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DiskFactory : System.Object {

    private List<GameObject> used = new List<GameObject>();
    private List<GameObject> free = new List<GameObject>();
    private List<Color> c = new List<Color>();
    private int count = 0;
    private int flag;
    private static DiskFactory _instance;

    public void GetDisk(int ruler)
    {
            GameObject disk;
            if (free.Count == 0)
            {
                disk = GameObject.Instantiate(Resources.Load<GameObject>("prefabs/disk"));
                disk.name = "disk" + count;
                count++;
                used.Add(disk);
            }
            else
            {
                disk = free[0];
                disk.SetActive(true);
                used.Add(disk);
                free.RemoveAt(0);
            }
            flag = UnityEngine.Random.Range(0, 5);
            disk.GetComponent<Renderer>().material.color = c[(ruler + flag) % 9];
            disk.transform.localScale = getSize(ruler);
            disk.transform.position = getPosition(ruler + flag);
            DiskData data = disk.GetComponent(typeof(DiskData)) as DiskData;
            data.speed = ruler + 1;
            data.target = getTarget(ruler + flag);     
    }

    public int GetCount()
    {
        return used.Count;
    }

    private Vector3 getTarget(int ruler)
    {
        if (ruler % 2 == 0)
        {
            return new Vector3(100, 10 + ruler, 60);
        }
        return new Vector3(-100, 10+ruler, 60);
    }

    private Vector3 getPosition(int ruler)
    {       
            if(ruler % 2 == 0)
            {
                return new Vector3(-10, 0, -6);
            }
            else
            {
                return new Vector3(10, 0, -6);
            }        
    }

    private Vector3 getSize(int r)
    {
        if(r == 0)
        {
            return new Vector3(5, 0.1f, 5);
        }
        else if(r == 1)
        {
            return new Vector3(4, 0.1f, 4);
        }
        else if (r == 2)
        {
            return new Vector3(3, 0.1f, 3);
        }
        else if (r == 3)
        {
            return new Vector3(2, 0.1f, 2);
        }
        else
        {
            return new Vector3(1, 0.1f, 1);
        }
    }

    public static DiskFactory GetInstance()
    {
        if (_instance == null)
        {
            _instance = new DiskFactory();
            _instance.c.Add(Color.black);
            _instance.c.Add(Color.blue);
            _instance.c.Add(Color.cyan);
            _instance.c.Add(Color.gray);
            _instance.c.Add(Color.green);
            _instance.c.Add(Color.magenta);
            _instance.c.Add(Color.red);
            _instance.c.Add(Color.white);
            _instance.c.Add(Color.yellow);
        }
        return _instance;
    }

    public void FreeDisk(GameObject disk)
    {
        disk.SetActive(false);
        free.Add(disk);
        used.Remove(disk);
        
    }

	// Use this for initialization
	void Start () {
        
    }
}
  • 飞碟数据信息 DiskData.cs:
    这个类主要用于记录和设置飞碟的相关属性,挂在飞碟的预制体上即可。具体实现如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DiskData : MonoBehaviour {

    public Vector3 target;
    public float speed;
    public diskAction action;

    // Use this for initialization
    void Start () {
        action = SSDirector.getInstance().currentSceneController as diskAction;
    }
	
	// Update is called once per frame
	void Update () {
        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 = Camera.main; //cam.GetComponent<Camera> ();
            Ray ray = ca.ScreenPointToRay(Input.mousePosition);

            //Return the ray's hit
            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                print(hit.transform.gameObject.name);
                if (hit.collider.gameObject.tag.Contains("Finish"))
                { //plane tag
                    Debug.Log("hit " + hit.collider.gameObject.name + "!");
                }
                //消失
                action.Boomshakalaka(this.gameObject);
            }
        }
        this.transform.position = Vector3.MoveTowards(this.transform.position, target, speed * Time.deltaTime * 2);
        if(this.transform.position == target)
        {            
            action.Drop(this.gameObject);
        }
    }
}
  • 回合控制类 RoundController.cs:
    这个类主要负责飞碟回合的控制。具体实现如下:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RoundController : MonoBehaviour, IsceneController,diskAction {

    public ScoreRecorder scoreRecorder = new ScoreRecorder();
    
    private int trial = 1;
    private int round = 0;
    
    bool game_over = false;
    bool next_round = false;
    private DiskFactory df = DiskFactory.GetInstance();
    private int score;
    
    void Awake()
    {
        SSDirector director = SSDirector.getInstance();
        director.setFPS(60);
        director.currentSceneController = this;
        director.currentSceneController.LoadResources();
    }

    public void LoadResources()
    {
        df.GetDisk(round);
        
    }

    public void Boomshakalaka(GameObject disk)
    {      
        df.FreeDisk(disk);
        
        if (df.GetCount() == 0)
        {
            next_round = true;
            scoreRecorder.AddScore(round + 1);
            trial = 1;
        }       
    }

    public void Drop(GameObject disk)
    {
        trial++;
        if(trial > 10)
        {
            game_over = true;
            df.FreeDisk(disk);
        }
        else
        {
            df.FreeDisk(disk);
            df.GetDisk(round);           
        }
    }

	// Use this for initialization
	void Start () {
       
    }

    // Update is called once per frame
    void Update () {
        score = scoreRecorder.getScore();
	}

    void OnGUI()
    {

        GUI.Box(new Rect(Screen.width / 2 - 75, 10, 150, 55), "Round " + (round + 1) + "\nYour score:  " + score + "\nYour trial left:  " + (11-trial));
        if(next_round)
        {
            GUI.Window(0, new Rect(Screen.width / 2 - Screen.width / 12, Screen.height / 2 - Screen.height / 12, Screen.width / 6, Screen.height / 6), next_round_window, "Success !");
        }
        if (game_over)
        {
            GUI.Window(0, new Rect(Screen.width / 2 - Screen.width / 12, Screen.height / 2 - Screen.height / 12, Screen.width / 6, Screen.height / 6), game_over_window, "Game Orver!");
        }
    }

    void next_round_window(int id)
    {
        if (GUI.Button(new Rect(Screen.width / 24, Screen.height / 24 + 5, Screen.width / 12, Screen.height / 12), "Next"))
        {
            NextRound();
        }
    }

    private void NextRound()
    {
        round++;
        next_round = false;
        df.GetDisk(round);       
    }

    void game_over_window(int id)
    {
        if (GUI.Button(new Rect(Screen.width / 24, Screen.height / 24 + 5, Screen.width / 12, Screen.height / 12), "Restart"))
        {
            Debug.Log("Restart");

            Restart();
        }
    }

    private void Restart()
    {      
        trial = 1;
        round = 0;
        
        game_over = false;
        next_round = false;
        
        scoreRecorder.Reset();
        df.GetDisk(round);
    }
}
  • 记分员类 ScoreRecorder.cs:
    这个类主要用于飞碟计分工作,包括加分和重置分数,根据飞碟的不同种类进行计分。具体实现如下:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ScoreRecorder : System.Object
{
    private int score = 0;
    public void AddScore(int s)
    {
        score = score + s;
    }
    public int getScore()
    {
        return score;
    }
    public void Reset()
    {
        score = 0;
    }
}
  • 导演类 SSDirector.cs:
    具体实现如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SSDirector : System.Object
{
    // singlton instance
    private static SSDirector _instance;

    public IsceneController currentSceneController { get; set; }
    public bool running { get; set; }

    //get instance anytime anywhere!
    public static SSDirector getInstance()
    {
        if (_instance == null)
        {
            _instance = new SSDirector();
        }
        return _instance;
    }

    public int getFPS()
    {
        return Application.targetFrameRate;
    }

    public void setFPS(int fps)
    {
        Application.targetFrameRate = fps;
    }
}
  • 用户交互类 Interface.cs:
    这个类主要用于用户交互。具体实现如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public enum Direction { Left, Right, Unfixed};

public interface IsceneController
{
    void LoadResources();
}

public interface diskAction
{
    void Boomshakalaka(GameObject disk);
    void Drop(GameObject disk);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值