3D游戏:五、与游戏世界交互

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

游戏内容要求:

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

游戏的要求:

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

UML:
在这里插入图片描述
伪代码:

getDisk(ruler) 
BEGIN
	IF (free list has disk) THEN
	a_disk = remove one from list
	ELSE
	a_disk = clone from Prefabs
	ENDIF
	Set DiskData of a_disk with the ruler
	Add a_disk to used list
	Return a_disk
END
FreeDisk(disk)
BEGIN
	Find disk in used list
	IF (not found) THEN THROW exception
	Move disk from used to free list
END

代码实现:
在这里插入图片描述

  • DiskData:包括射击飞碟的得分、飞碟颜色、初始的位置、大小。
public class DiskData : MonoBehaviour{
    public int score = 1;                               
    public Color color = Color.white;                   
    public Vector3 direction;                          
    public Vector3 scale = new Vector3( 1 ,0.25f, 1);   
}
  • 飞碟管理员DiskFactory:负责接收SSDirector的请求,使用模板模式根据预制和规则制作飞碟、回收飞碟。
    维护两个List,分别储存正在使用的飞碟和空闲的飞碟:
    public GameObject disk_prefab = null;   //飞碟预制体
    private List<DiskData> used = new List<DiskData>(); //正在被使用的飞碟列表
    private List<DiskData> free = new List<DiskData>(); //空闲的飞碟列表

GetDisk:根据规则在空闲列表中选择飞碟,若空闲列表中没有要选择的飞碟,则重新实例化飞碟。

public GameObject GetDisk(int round){
        int choice = 0;
        int scope1 = 1, scope2 = 4, scope3 = 7;           //随机的范围
        float start_y = -10f;                             //刚实例化时的飞碟的竖直位置
        string name;
        disk_prefab = null;
        //根据回合,随机选择要飞出的飞碟
        if (round == 1) choice = Random.Range(0, scope1);
        else if(round == 2) choice = Random.Range(0, scope2);
        else choice = Random.Range(0, scope3);
        
        //将要选择的飞碟的name
        if(choice <= scope1) name = "disk1";
        else if(choice <= scope2 && choice > scope1) name = "disk2";
        else name = "disk3";
	//寻找相同name的空闲飞碟
        for(int i=0;i<free.Count;i++) if(free[i].name == name){
            disk_prefab = free[i].gameObject;
            free.Remove(free[i]);
            break;
        }
        //如果空闲列表中没有,则重新实例化飞碟
        if(disk_prefab == null){
            if (name == "disk1") disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk1"), new Vector3(0, start_y, 0), Quaternion.identity);
            else if (name == "disk2") disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk2"), new Vector3(0, start_y, 0), Quaternion.identity);
            else disk_prefab = Instantiate(Resources.Load<GameObject>("Prefabs/disk3"), new Vector3(0, start_y, 0), Quaternion.identity);
            
            //给新实例化的飞碟赋予其他属性
            float ran_x = Random.Range(-1f, 1f) < 0 ? -1 : 1;
            disk_prefab.GetComponent<Renderer>().material.color = disk_prefab.GetComponent<DiskData>().color;
            disk_prefab.GetComponent<DiskData>().direction = new Vector3(ran_x, start_y, 0);
            disk_prefab.transform.localScale = disk_prefab.GetComponent<DiskData>().scale;
        }
        //添加到使用列表中
        used.Add(disk_prefab.GetComponent<DiskData>());
        return disk_prefab;
    }

FreeDisk:回收飞碟。

 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.cs
ublic 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;
        }
    }
}
  • 记分员ScoreRecorder:start初始化分数为0,每击落一个飞碟加上对应的分数,重新开始时重置分数。
public class ScoreRecorder : MonoBehaviour{
    public int score;                   //分数
    void Start (){
        score = 0;
    }
    //记录分数
    public void Record(GameObject disk){
        int temp = disk.GetComponent<DiskData>().score;
        score = temp + score;
    }
    //重置分数
    public void Reset(){
        score = 0;
    }
}
  • FlyDisk:抛出飞碟,继承SSAction实现。
 public static UFOFlyAction GetSSAction(Vector3 direction, float angle, float power){
        //初始化物体将要运动的初速度向量
        UFOFlyAction action = CreateInstance<UFOFlyAction>();
        if (direction.x == -1) action.start_vector = Quaternion.Euler(new Vector3(0, 0, -angle)) * Vector3.left * power;
        else action.start_vector = Quaternion.Euler(new Vector3(0, 0, angle)) * Vector3.right * power;
        return action;
    }
    
    public override void Update(){
        //计算物体的向下的速度,v=at
        time += Time.fixedDeltaTime;
        gravity_vector.y = gravity * time;

	//位移模拟
        transform.position += (start_vector + gravity_vector) * Time.fixedDeltaTime;
        current_angle.z = Mathf.Atan((start_vector.y + gravity_vector.y) / start_vector.x) * Mathf.Rad2Deg;
        transform.eulerAngles = current_angle;
	
	//如果物体y坐标小于-10,动作就做完了
        if (this.transform.position.y < -10){
            this.destroy = true;
            this.callback.SSActionEvent(this);      
        }
    }	

运行结果:
在这里插入图片描述
在这里插入图片描述
击中爆炸效果:在这里插入图片描述
生命值为0时,游戏结束:在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值