3Dhw5与游戏世界交互

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

游戏内容要求:

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

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

参考老师给出的UML图
UML

游戏简介

在这个游戏中,一共有三个关卡,第一个关卡只有红色的飞碟,一个飞碟是1分。当得到15分后,即进入第二关,新加入黄色飞碟,击中一个得2分。当得到30分之后,即进入第三关,新加入蓝色飞碟,击中一个得3分。当错过了10个飞碟的话(即X减少到无),游戏结束。

具体函数实现

首先介绍FirstController:
继承MonoBehavior,ISceneController,IUserAction。主要函数如下:

   void Start ()
    {
    //获取各个实例及各个类
        SSDirector director = SSDirector.getInstance();     
        director.currentScenceController = this;             
        diskFactory = Singleton<DiskFactory>.Instance;
        scoreRecorder = Singleton<ScoreRecorder>.Instance;
        actionManager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;
        userGUI = gameObject.AddComponent<UserGUI>() as UserGUI;
    }
	
	void Update ()
    {
        if(isGameStart)
        {
            if (isGameOver)
            {
                CancelInvoke("LoadResources");
            }
            if (!isPlayGame)
            {
                InvokeRepeating("LoadResources", 1f, speed);
                isPlayGame = true;
            }
            SendDisk();
            if (scoreRecorder.score >= scoreRound2 && round == 1)
            {
                round = 2;
                speed = speed - 0.6f;
                CancelInvoke("LoadResources");
                isPlayGame = false;
            }
            else if (scoreRecorder.score >= scoreRound3 && round == 2)
            {
                round = 3;
                speed = speed - 0.5f;
                CancelInvoke("LoadResources");
                isPlayGame = false;
            }
        }
    }
//加载资源
    public void LoadResources()
    {
        diskQueue.Enqueue(diskFactory.GetDisk(round)); 
    }
//发送飞碟
    private void SendDisk()
    {
        float position_x = 16;                       
        if (diskQueue.Count != 0)
        {
            GameObject disk = diskQueue.Dequeue();
            diskNotHit.Add(disk);
            disk.SetActive(true);
            float ran_y = Random.Range(1f, 4f);
            float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
            disk.GetComponent<DiskData>().direction = new Vector3(ran_x, ran_y, 0);
            Vector3 position = new Vector3(-disk.GetComponent<DiskData>().direction.x * position_x, ran_y, 0);
            disk.transform.position = position;
            float power = Random.Range(10f, 15f);
            float angle = Random.Range(15f, 28f);
            actionManager.UFOFly(disk,angle,power);
        }

        for (int i = 0; i < diskNotHit.Count; i++)
        {
            GameObject temp = diskNotHit[i];
            if (temp.transform.position.y < -10 && temp.gameObject.activeSelf == true)
            {
                diskFactory.FreeDisk(diskNotHit[i]);
                diskNotHit.Remove(diskNotHit[i]);
                userGUI.BloodReduce();
            }
        }
    }
//击打飞碟
    public void Hit(Vector3 pos)
    {
        Ray ray = Camera.main.ScreenPointToRay(pos);
        RaycastHit[] hits;
        hits = Physics.RaycastAll(ray);
        bool not_hit = false;
        for (int i = 0; i < hits.Length; i++)
        {
            RaycastHit hit = hits[i];
            if (hit.collider.gameObject.GetComponent<DiskData>() != null)
            {
                for (int j = 0; j < diskNotHit.Count; j++)
                {
                    if (hit.collider.gameObject.GetInstanceID() == diskNotHit[j].gameObject.GetInstanceID())
                    {
                        not_hit = true;
                    }
                }
                if(!not_hit)
                {
                    return;
                }
                diskNotHit.Remove(hit.collider.gameObject);
                scoreRecorder.Record(hit.collider.gameObject);
                Transform explode = hit.collider.gameObject.transform.GetChild(0);
                explode.GetComponent<ParticleSystem>().Play();
                StartCoroutine(WaitingParticle(0.08f, hit, diskFactory, hit.collider.gameObject));
                break;
            }
        }
    }
//统计得分
    public int GetScore()
    {
        return scoreRecorder.score;
    }

    public void Restart()
    {
        isGameOver = false;
        isPlayGame = false;
        scoreRecorder.score = 0;
        round = 1;
        speed = 2f;
    }

    public void GameOver()
    {
        isGameOver = true;
    }

  • ScoreRecord:
    这个脚本主要是实现计分功能
public class ScoreRecorder : MonoBehaviour {
    public int score;
    // Use this for initialization
    void Start () {
        score = 0;
    }

    public void Record(GameObject disk)
    {
        int temp = disk.GetComponent<DiskData>().score;
        score += temp;
    }

    public void Reset()
    {
        score = 0;
    }
}
  • 飞碟自身的函数和飞碟工厂
public class DiskData : MonoBehaviour {

    public int score = 1;
    public Vector3 direction;
    public Vector3 scale = new Vector3(1, 1, 1);
}
public class DiskFactory : MonoBehaviour {
    public GameObject diskPrefab = null;
    private List<DiskData> used = new List<DiskData>();
    private List<DiskData> free = new List<DiskData>();

    public GameObject GetDisk(int round)
    {
        int choice = 0;
        int scope = 1, scope1 = 4, scope2 = 7;
        float startY = -10f;
        string tag;
        diskPrefab = null;

        if(round == 1)
            choice = Random.Range(0, scope);
        else if(round == 2)
            choice = Random.Range(0, scope1);
        else if(round == 3)
            choice = Random.Range(0, scope2);

        if(choice <= scope)
            tag = "disk1";
        else if(choice <= scope && choice > scope1)
            tag = "disk2";
        else
            tag = "disk3";

        for(int i = 0; i < free.Count; i++)
        {
            if(free[i].tag == tag)
            {
                diskPrefab = free[i].gameObject;
                free.Remove(free[i]);
                break;
            }
        }
        if(diskPrefab == null)
        {
            if (tag == "disk1")
                diskPrefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk1"), new Vector3(0, startY, 0), Quaternion.identity);
            else if(tag == "disk2")
                diskPrefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk2"), new Vector3(0, startY, 0), Quaternion.identity);
            else if(tag == "disk3")
                diskPrefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk3"), new Vector3(0, startY, 0), Quaternion.identity);

            float ranX = Random.Range(-1f, -1f) < 0 ? -1 : 1;
            diskPrefab.GetComponent<DiskData>().direction = new Vector3(ranX, startY, 0);
            diskPrefab.transform.localScale = diskPrefab.GetComponent<DiskData>().scale;
        }
        used.Add(diskPrefab.GetComponent<DiskData>());
        return diskPrefab;
    }

    public void FreeDisk(GameObject disk)
    {
        for (int i = 0; i < used.Count; i++)
        {
            if (disk.GetInstanceID() == used[i].gameObject.GetInstanceID())
            {
                used[i].gameObject.SetActive(false);
                free.Add(used[i]);
                used.Remove(used[i]);
                break;
            }
        }
    }

}
  • Singleton:实现飞碟工厂的单例
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;
        }
    }
}
  • 实现界面的UserGUI
void OnGUI ()
    {
        boldStyle.normal.textColor = new Color(1, 0, 0);
        boldStyle.fontSize = 20;
        textStyle.normal.textColor = new Color(0,0,0, 1);
        textStyle.fontSize = 20;
        scoreStyle.normal.textColor = new Color(1,0,1,1);
        scoreStyle.fontSize = 20;
        overStyle.normal.textColor = new Color(1, 0, 0);
        overStyle.fontSize = 25;

        if (gameStart)
        {
            //用户射击
            if (Input.GetButtonDown("Fire1"))
            {
                Vector3 pos = Input.mousePosition;
                action.Hit(pos);
            }

            GUI.Label(new Rect(10, 5, 200, 50), "Score:", textStyle);
            GUI.Label(new Rect(55, 5, 200, 50), action.GetScore().ToString(), scoreStyle);

            GUI.Label(new Rect(Screen.width - 120, 5, 50, 50), "Life:", textStyle);
            //显示当前血量
            for (int i = 0; i < life; i++)
            {
                GUI.Label(new Rect(Screen.width - 75 + 10 * i, 5, 50, 50), "X", boldStyle);
            }
            //游戏结束
            if (life == 0)
            {
                score = action.GetScore();
                GUI.Label(new Rect(Screen.width / 2 - 20, Screen.width / 2 - 250, 100, 100), "Game Over", overStyle);
                GUI.Label(new Rect(Screen.width / 2 - 10, Screen.width / 2 - 200, 50, 50), "Score:", textStyle);
                GUI.Label(new Rect(Screen.width / 2 + 50, Screen.width / 2 - 200, 50, 50), score.ToString(), textStyle);
                if (GUI.Button(new Rect(Screen.width / 2 - 20, Screen.width / 2 - 150, 100, 50), "Restart"))
                {
                    life = 10;
                    action.Restart();
                    return;
                }
                action.GameOver();
            }
        }
        else
        {
            GUI.Label(new Rect(Screen.width / 2 - 30, Screen.width / 2 - 350, 100, 100), "Hit UFO!", overStyle);
            if (GUI.Button(new Rect(Screen.width / 2 - 25, Screen.width / 2 - 250, 100, 50), "Game Start"))
            {
                gameStart = true;
                action.BeginGame();
            }
        }
    }
    public void BloodReduce()
    {
        if(life > 0)
            life--;
    }
游戏演示视频

3D游戏编程与设计第五次作业演示视频

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值