鼠标打飞碟游戏

本文介绍了使用Unity编写鼠标打飞碟游戏的相关内容。游戏有多个round,每个round含10次trial,难度随round上升。采用带缓存的工厂模式、MVC架构和adapter模式设计。详细阐述了各代码文件功能,如Direct.cs、FirstController.cs、UFOController.cs等,还说明了项目使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

游戏效果:

鼠标打飞碟游戏视频

一、作业要求
1、编写一个简单的鼠标打飞碟(Hit UFO)游戏。
游戏内容要求:

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

使用带缓存的工厂模式管理不同飞碟的生产与回收,该工厂必须是场景单实例的!具体实现见参考资源 Singleton 模板类;
近可能使用前面 MVC 结构实现人机交互与游戏模型分离;
按 adapter模式 设计图修改飞碟游戏,使它同时支持物理运动与运动学(变换)运动。

二、设计结构

本次使用了MVC架构,Driect.cs文件中包含了Direct,UserAction和ISceneController三个类的定义。就MVC结构而言,其作用非常重要。

代码:

Direct.cs

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

public class Direct : System.Object
{
    private static Direct instance;
    public ISceneController currentScene{get;set;}
    public static Direct getInstance(){
        if (instance==null)
            instance=new Direct();
        return instance;
    }

}

public interface ISceneController{
    
}
public interface UserAction{
    void getScore(int n);
    void getLost(int n);

}

Direct类负责管理当前场景的指示器,执行一些参数传递操作(使用instance明确当前场景指示器),以及管理其他重要功能。

ISceneController定义了每个场景指示器必须具备的函数,尽管此处为空。ISceneController的主要作用是标记,这样其他类中可以通过Direct找到当前的控制器。

UserAction规定了用户可能触发的事件。在复杂的情况下,可以为每个场景指示器定义一个Action接口。这里的GetScore表示玩家获得分数的事件,而Getlost表示玩家失去分数的事件(未击中飞碟)。

FirstController.cs

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

public class FirstController : MonoBehaviour,ISceneController,UserAction
{
    // Start is called before the first frame update
    public GameObject[] terrain;
    public UFOController uc;
    public int score;
    public int lost;
    public yesyes[] yes;

    public GUIStyle style;

    public int round;



    void Start()
    {
        Direct.getInstance().currentScene=this;
        loadResources();
        Random.InitState((int)System.DateTime.Now.Ticks);
        uc.StartRound();
        score=0; lost=0;
        round=1;


    }

    // Update is called once per frame
    
    void Update()
    {
        if (round>=4) return;
        uc.UFOUpdate();

        if (uc.isOver()==1){
            round++;
            if (round>=4) 
                return;
            uc.setDifficulty(round);
            uc.StartRound();
        }  

    }

    void OnGUI(){
        if (round>=4){
            style.alignment=TextAnchor.MiddleCenter;
            style.normal.textColor=Color.black;
            style.fontSize=250;
            if (score>lost){
                GUI.Label(new Rect(0,0,Screen.width,Screen.height),"你赢了",style);
            }else{
                GUI.Label(new Rect(0,0,Screen.width,Screen.height),"你输了",style);
            }
        }

        style.alignment=TextAnchor.MiddleLeft;
        style.normal.textColor=Color.black;
        style.fontSize=40;
        GUI.Label(new Rect(0,Screen.height-100,Screen.width,100),"                                     Score : "+score.ToString()+"       Lost : "+lost.ToString()+"              Round "+round.ToString(),style);
    
        for(int i=1;i<=33;++i){
            if (yes[i].isOn==1){
                if (Time.time-yes[i].stime>=3){
                    yes[i].isOn=0;
                    continue;
                }
                else{
                    if (yes[i].color=="white") style.normal.textColor=Color.gray;
                    if (yes[i].color=="blue") style.normal.textColor=Color.blue;
                    if (yes[i].color=="red") style.normal.textColor=Color.red;

                    GUI.Label(new Rect(yes[i].x,yes[i].y,400,200),"A",style);
                }
            }
        }

    }

    public void loadResources(){
        terrain=new GameObject[4];
        terrain[0]=Instantiate(Resources.Load<GameObject>("Terrain"),new Vector3(0,0,0),Quaternion.identity);
        terrain[1]=Instantiate(terrain[0],new Vector3(-1000,0,0),Quaternion.identity);
        terrain[2]=Instantiate(terrain[0],new Vector3(-1000,0,-1000),Quaternion.identity);
        terrain[3]=Instantiate(terrain[0],new Vector3(0,0,-1000),Quaternion.identity);

        uc=new UFOController();
        yes=new yesyes[34];
        for (int i=0;i<=33;++i){
            yes[i]=new yesyes();
        }
    }

    public void getScore(int n){
        score+=n;

        for (int i=1;i<=33;++i){
            if (yes[i].isOn==0){
                yes[i].isOn=1;
                yes[i].x=Input.mousePosition.x;
                yes[i].y=Screen.height-Input.mousePosition.y;
                yes[i].stime=Time.time;

                if (n==1) yes[i].color="white";
                if (n==2) yes[i].color="blue";
                if (n==3) yes[i].color="red";

                break;
            }
        }
    }

    public void getLost(int n){
        lost+=n;
    }


}


public class yesyes{
    public int isOn;
    public float x,y;
    public float stime;
    public string color;

    public yesyes(){
        isOn=0;
        x=-200;y=-200;
        stime=-100;
        color="white";
    }

    public void over(){
        isOn=0;
    }
}

在FirstController中,首先在Start函数中建立了与Direct类的关联,并加载了相应的资源。

在Update函数中调用了UFOController类中的UFOUpdate函数,该函数用于管理所有UFO,以减轻场记的代码负担和混乱程度。此外,Update函数还对游戏结束条件进行了判断。由于游戏分为三个关卡,随着关卡的增加,游戏难度也会提高。因此,Update函数需要处理这些变化。

FirstController还实现了UserAction接口的函数定义,用于获取或失去分数。

关于yesyes类:

作用:在鼠标点击飞碟后,希望屏幕上显示一个与飞碟同颜色的“A”字样,以提醒击中了飞碟。

参数:记录了每个“yesyes”字样应有的颜色、位置以及是否应该显示;确定其开始显示的时间。在OnGui函数中对这些参数进行判断,如果显示时间超过三秒,则停止显示。

UFOController.cs

负责管理UFO的生成、移动和销毁等所有事务。此文件还包括其他与UFO相关的类的定义,用于记录UFO的信息或管理UFO的点击事件。

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

public class UFOController :MonoBehaviour
{
    
    public GameObject white,blue,red;
    public UFO[] ufos;
   
    public int diff;

    public float stime;

    private float lastCreateTime;
    public void UFOUpdate(){
        int number=getNumber();
        
        if (Time.time-stime>=9.5f){
            
        }else{
            if (Time.time-lastCreateTime>=1){
                lastCreateTime=Time.time;

                for (int i=1;i<=diff;++i){
                    CreateUFO();
                }
            }
        }

        for(int i=1;i<=33;++i){
            if (ufos[i].obj!=null){
                ufos[i].obj.transform.position=Vector3.MoveTowards(ufos[i].obj.transform.position,ufos[i].target,ufos[i].speed);
                if (ufos[i].obj.transform.position==ufos[i].target || ufos[i].obj.transform.position.y<=4.5){
                    ufos[i].obj.GetComponent<UFOClass>().getOut();
                    number--;
                }
            }
        }
        
    }

    public void StartRound(){
        stime=Time.time;
    }

    public int isOver(){
        if (getNumber()==0 && Time.time-stime>=10){
            return 1;
        }
        return 0;
    }

    public UFOController(){
        white=Resources.Load<GameObject>("whiteUFO");
        blue=Resources.Load<GameObject>("blueUFO");
        red=Resources.Load<GameObject>("redUFO");

        ufos=new UFO[34];
        for (int i=0;i<34;++i){
            ufos[i]=new UFO();
        }
        diff=1;
        stime=-100;
        lastCreateTime=-100;
    }
    public void setDifficulty(int d){
        diff=d;
        
    }


    public int getNumber(){
        int ans=0;
        for (int i=1;i<=33;++i){
            if (ufos[i].obj!=null)
                ans++;
        }
        return ans;
    }

    public void CreateUFO(){
        UFO u=new UFO();
        for (int i=1;i<=33;++i){
            if (ufos[i].obj==null){
                u=ufos[i];
                break;
            }
        }


        u.init(diff);

        int kind;
        kind=Random.Range(0,100);
        if (kind<=33){
            u.obj=Instantiate(white,u.pos,Quaternion.identity);
            u.obj.name="white";
        }
        else if (kind<=66){
            u.obj=Instantiate(blue,u.pos,Quaternion.identity);
            u.obj.name="blue";
        }
        else{
            u.obj=Instantiate(red,u.pos,Quaternion.identity);
            u.obj.name="red";
        }

        u.obj.AddComponent(typeof(UFOClass));
    }
}

public class UFO {
    public float speed;
    public Vector3 target;
    public GameObject obj;
    public Vector3 pos;
   
    public UFO(){

    }

    public void init(int diff){
        Object.Destroy(obj);
        float y=Random.Range(60,120f);
        float x=150;
        if (Random.Range(0,2)==0){
            x=-x;
        }
        pos=new Vector3(x,y,80);

        x=-x;
        y=Random.Range(0f,70f);
        target=new Vector3(x,y,80);
        

        speed=Random.Range(0.04f,0.06f);
        speed=speed*diff;
    }
}


public class UFOClass:MonoBehaviour{
    private UserAction action;

    public UFOClass(){
        action=Direct.getInstance().currentScene as UserAction;
    }

    void OnMouseDown(){
        string s=gameObject.name;
        if (s=="white"){
            action.getScore(1);
        }
        if (s=="blue"){
            action.getScore(2);
        }
        if (s=="red"){
            action.getScore(3);
        }
        Destroy(gameObject);
        
    }

    public void getOut(){
        string s=gameObject.name;
        if (s=="white"){
            action.getLost(1);
        }
        if (s=="blue"){
            action.getLost(2);
        }
        if (s=="red"){
            action.getLost(3);
        }
        Destroy(gameObject);        
    }
}

根据算法,游戏同屏最多出现33个飞碟,故用一个数组存储。

CreateUFO用于创建UFO,根据难度值,对UFO的速度进行调整。
StartRound用于重新开始一个回合
SetDifficulty用于设置难度,一般用在每个回合开始前。
getNumber统计当前UFO的个数。
isOver判断当前回合有没有结束。如果飞碟数为0,并且已经开始了一段时间(防止刚开始就结束),那么此回合就结束了。将有FirstController开启下一回合。
UFOUpdate在FirstController中的Update中被调用,两者的调用是同步进行的。UFOUpdate主要用于管理飞碟的移动,判断创建新飞碟的条件,并且判断飞碟出界或者到达目的地的条件,是这个类的核心。

下面的UFO和UFOClass两个类:

UFO类是为了记录每一个飞碟的信息,比如初始位置,速度,目的地等,还有一个obj作为指针指向飞碟的GameObject。内部inti函数会根据当前的难度进行重新构造,完成了资源的重新利用。

UFOClass主要用于AddComponent函数,给飞碟的GameObject加入组件。该组件用于处理点击事件。里面的action变量是利用MVC结构的特性,从而完成了对场记中玩家事件函数的调用。

三、项目使用方法

下载Asserts.zip解压,用Asserts原项目的Asserts。打开Unity,双击UFOScene,点击运行。


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值