3D游戏作业:与游戏世界交互

3D游戏作业:与游戏世界交互

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

游戏内容要求:

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

游戏的要求:

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

实现内容:

这次的编程作业主要是实现交互内容,其他的要求例如:物件的移动和MVC架构在之前的作业都实现过了,所以不太难,但是我这次采用的MVC架构是初始版MVC,并没有采用动作分离,因为我认为打飞碟这个游戏的动作本身就不是很多,没有必要采用动作分离架构。

游戏界面如下:
在这里插入图片描述
游戏可以实现暂停和重新开始功能,游戏总共有3个round,难度依次递增。每个round有10个trial(我理解的trial就是生成飞碟。。 不知道对不对),有三种飞碟分别对应不同的分值,分值最大的飞碟体积更小,难度更高。每个飞碟的碰撞体积只有一个,为了提升性能,我用了长方体碰撞体积而不是完全仿照飞碟形状,这样做虽然会略失判断精度,但可以提升性能。

游戏第三个Round结束后会跳到第四个Round,但此时游戏已经终止,只能重新开始。

生成飞碟的工厂我遵循了单例模式:

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

public class Sing : MonoBehaviour
{
    protected static Factory instance;

    public static Factory Instance{
        get{
            if(instance == null){
                instance = (Factory)FindObjectOfType(typeof(Factory));
                // Debug.Log("asdaszzzz");
            }
        return instance;
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

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

public class Factory : MonoBehaviour
{
    public List<GameObject> UFO_on = new List<GameObject>();
    public List<GameObject> UFO_off = new List<GameObject>();


    public void genUFO()    //借鉴了网上的博客
    {
        // Debug.Log("gen!");
        GameObject ufo;
        if(UFO_off.Count == 0)
        {
            float num = Random.Range(0f,9f);
            if(num > 6)
                ufo = Instantiate(Resources.Load("Prefabs/ufo1"), Vector3.zero, Quaternion.identity) as GameObject;
            else if(num > 3)
                ufo = Instantiate(Resources.Load("Prefabs/ufo2"), Vector3.zero, Quaternion.identity) as GameObject;
            else
                ufo = Instantiate(Resources.Load("Prefabs/ufo3"), Vector3.zero, Quaternion.identity) as GameObject;
        }
        else
        {
            ufo = UFO_off[0];
            UFO_off.RemoveAt(0);
        }
        float x = Random.Range(-8f,8f);
        float y = Random.Range(-5f,5f);
        float z = Random.Range(-2f,2f);
        ufo.transform.position = new Vector3(x, y ,0);
        ufo.transform.Rotate(new Vector3(x < 0 ? -x*60 : x*60, y < 0 ? -y*60:y*60, z<0?-z*20:z*20));
        UFO_on.Add(ufo);
    }

    public void recycleUFO(GameObject obj)
    {
        for(int i=0; i < UFO_on.Count ;i++){
            if(UFO_on.ToArray()[i] == obj){
                UFO_on.RemoveAt(i);
            }
        }
        obj.transform.position = new Vector3(100,100,100);
        UFO_off.Add(obj);
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

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

MVC架构如下:
Controller:

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

public class Controller : System.Object
{
    private static Controller _instance;

    public Model currentModel {get; set;}
    public bool running{get; set;}

    public static Controller getInstance(){
        if(_instance == null){
            _instance = new Controller();
        }
        return _instance;
    }

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

    public void setFPS(int fps){
        Application.targetFrameRate = fps;
    }

    // Start is called before the first frame update
    void Start()
    {
        
    }

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

View:

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

public interface IUserAction
{
    void GameOver();
}

public class View : MonoBehaviour
{
    private Model action;
    private GUIStyle fontstyle = new GUIStyle();
    private GUIStyle fontstyle1 = new GUIStyle();

    // Start is called before the first frame update
    void Start()
    {
        action = Controller.getInstance().currentModel as Model;
        fontstyle.fontSize = 30;
        fontstyle.normal.textColor = Color.yellow;
        fontstyle1.fontSize = 30;
        fontstyle1.normal.textColor = Color.yellow;
    }

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

    public void OnGUI(){
        GUI.Label(new Rect(10,40,200,200),"Score: " + action.score, fontstyle);
        GUI.Label(new Rect(10,10,200,200),"Round: " + action.round, fontstyle);
        GUI.Label(new Rect(10,70,200,200),"Trial: " + action.trial, fontstyle);
        if (GUI.Button(new Rect(10, 110, 100, 50), "Pause"))
        {
            action.Pause();
        }
        // if (GUI.Button(new Rect(10, 170, 100, 50), "Start"))
        // {
        //     action.Start();
        // }
        if (GUI.Button(new Rect(10, 170, 100, 50), "Restart"))
        {
            action.Restart();
        }        
    }

}

Model:

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

public interface ForModel{
    void LoadResources();
    void Pause();
    void Resume();
}

public class Model : MonoBehaviour, ForModel
{
    public int score;
    public int round;
    public int mytime;
    public int trial;
    Controller controller;
    public Factory myfactory;

    void Awake(){
        controller = Controller.getInstance();
        controller.setFPS(60);
        controller.currentModel = this;
        controller.running=true;
        LoadResources();
        score=0;
        round=1;
        trial=0;
        mytime=0;
        myfactory=Sing.Instance;
        InvokeRepeating("updatetime",1f,1f);
        InvokeRepeating("gentiral",1f,3f);
    }

    public void Start(){
        Debug.Log(myfactory);
        for(int i=0; i<10;i++){
            myfactory.genUFO();
        }
    }

    public void Restart(){
        controller.running=false;
        score=0;
        round=1;
        trial=0;
        mytime=0;
        List<GameObject> ufos = myfactory.UFO_on;
        List<GameObject> t= new List<GameObject>();
        foreach(GameObject ufo in ufos){
            t.Add(ufo);
        }
        ufos.Clear();
        foreach(GameObject st in t){
            myfactory.recycleUFO(st);
        }
        for(int i=0; i<10;i++){
            myfactory.genUFO();
        }
        controller.running=true;
    }

    public void Resume(){

    }

    public void Pause(){
        controller.running=!controller.running;
    }

    public void LoadResources(){

    }

    public void gentiral(){
        if(controller.running == false)
            return;
        trial+=1;
        while (myfactory.UFO_on.Count < 10){
            myfactory.genUFO();
        }
    }

    public void updatetime(){
        if(controller.running == false)
            return;
        mytime+=1;
        if(mytime>=30){
            mytime=0;
            trial=0;
            round+=1;
        }
        Debug.Log(mytime);
    }

    // Start is called before the first frame update

    // Update is called once per frame
    void Update()
    {
        if(controller.running == false)
            return;
        List<GameObject> ufos = myfactory.UFO_on;
        try{
        foreach(GameObject ufo in ufos) 
        {
            Vector3 v = new Vector3(ufo.transform.localRotation.x,ufo.transform.localRotation.y,ufo.transform.localRotation.z);
            ufo.transform.Translate(v*Time.deltaTime*round*2);
            if(ufo.transform.position.x > 10 || ufo.transform.position.x < -10){
                myfactory.recycleUFO(ufo);
            }
            if(ufo.transform.position.y > 10 || ufo.transform.position.y < -10){
                myfactory.recycleUFO(ufo);
            }
            if(ufo.transform.position.z < -10){
                myfactory.recycleUFO(ufo);
            }
        }
        }
        catch{

        }


        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;
			ca = Camera.main;

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

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

			foreach (RaycastHit hit in hits) {
                if(hit.transform.gameObject == null){
                    continue;
                }
                if(hit.transform.gameObject.name == "ufo1(Clone)"){
                    score += 1;
                }
                if(hit.transform.gameObject.name == "ufo2(Clone)"){
                    score += 2;
                }
                if(hit.transform.gameObject.name == "ufo3(Clone)"){
                    score += 3;
                }
				print (hit.transform.gameObject.name);
				if (hit.collider.gameObject.tag.Contains("Finish")) { //plane tag
					// Debug.Log ("hit " + hit.collider.gameObject.name +"!" ); 
				}
                myfactory.recycleUFO(hit.transform.gameObject);
                Debug.Log("on:"+myfactory.UFO_on.Count);
                Debug.Log("off"+myfactory.UFO_off.Count);
				// Destroy (hit.transform.gameObject.transform.parent.gameObject);
			}
		}	

    }
}

以上则是打飞碟游戏的实现,完整代码见github

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

用自定义组件定义几种飞碟,做成预制
参考官方脚本手册 https://docs.unity3d.com/ScriptReference/Editor.html
实现自定义组件,编辑并赋予飞碟一些属性

我做了三种飞碟component,它们的预制资源都在Resources/Prefabs中
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值