3D游戏编程与设计作业6-Unity实现打飞碟游戏改进版(Hit UFO)

改进飞碟(Hit UFO)游戏

游戏内容要求

  1. 按adapter模式设计图修改飞碟游戏
  2. 使它同时支持物理运动与运动学(交换)运动

编程实践

本次作业直接在上一次打飞碟游戏的基础上增加adapter设计模式(增加一个内含PhysisActionManagerCCActionManager接口的ActionManagers类),提供一个运动的统一接口,根据玩家的具体操作再决定是调用物理运动类还是运动学动作管理器类。也就是游戏同时存在两个飞碟运动模式,支持物理运动和运动学互相切换

  • Adapter(适配器)模式:将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。做法是将类自己的接口包裹在一个已存在的类中。有时也称为包装样式/包装。

本次作业采用的Adapter模式属于对象适配器模式,即adapter类持有不同的动作管理器对象,并实现对应的想提供的目标接口

本次游戏的结构UML图如下:
UML
玩家切换运动模式的方法是在游戏界面点击鼠标右键,但并不会马上中断游戏切换运动模式,而是在下一个trail开始时进行切换

动作接口

首先在原来的SceneController类中再定义一个统一的动作接口(模板类),包含了物理运动和运动学动作管理器类都需要实现的方法addForceOnUFO(GameObject UFO),适配器类Adapter继承该接口。

  public interface IActionManager
  {
      void addForceOnUFO(GameObject UFO);
  }
用户接口

当用户接口类UserInterface中的Update()方法检测到鼠标右键被按下时,调用方法切换动作管理器。修改后的UserInterfaceUpdate()方法如下:

    // Update is called once per frame
	void Update () {
        //检测鼠标点击(左键)
        if (Input.GetMouseButtonDown(0))
        {
            Vector3 mousePos = Input.mousePosition;
            action.hitUFO(mousePos);
        }
        //检测用户按下空格
        if (Input.GetKeyDown(KeyCode.Space))
        {
            action.launchUFO();
        }
        //检测鼠标右键点击
        if (Input.GetButtonDown("Fire2"))
        {
            action.switchActionModeNextRound();
        }
    }

相应地,在SceneController类的IUserAction接口中增加该switchActionInNextRound()方法的声明:

    public interface IUserAction
    {
        void launchUFO();
        void hitUFO(Vector3 mousePos);
        void switchActionModeNextRound();
    }
适配器类

接着编写最重要的适配器类UFOActionAdapter,该类需要实现IActionManager接口(实现不同模式的addForceOnUFO(GameObject UFO)方法的调用),并且持有PhysisActionManagerCCActionManager对象,用一个整数型成员变量WhichActionManager来记录当前使用的是哪一个动作管理器,值为0表示当前处于物理运动模式,值为1表示当前处于运动学动作管理器模式,只需要WhichActionManager = 1-WhichActionManager就能实现切换运动模式。代码如下:

    public class UFOActionAdapter : IActionManager
    {
        private PhysisActionManager physisActionManager;
        private CCActionManager ccActionManager;
        int whichActionManager = 0; // 0 -> PhysisActionManager, 1 -> CCActionManager

        public UFOActionAdapter()
        {
            physisActionManager = PhysisActionManager.getInstance();
            ccActionManager = CCActionManager.getInstance();
        }

        public void switchActionMode()
        {
            whichActionManager = 1 - whichActionManager;
        }

        public void addForceOnUFO(GameObject UFO)
        {
            if(whichActionManager == 0)
            {
                physisActionManager.addForce(UFO);
            }
            else
            {
                ccActionManager.addForce(UFO);
            }
        }
    }

在SceneController中使用bool型成员变量switchActionManager来标记是否需要切换运动模式,初始化为false,表示初始不做模式切换时游戏处于物理运动模式。代码如下:

private bool switchActionManager = false;

如果switchActionModeNextRound()方法被调用,则将switchActionManager置为true:

    public void switchActionModeNextRound()
    {
        myStatusCtrl.showSwitchText();
        switchActionManager = true;
    }

并且在每一轮trail开始时检测switchActionManager是否为true,若为true则切换运动模式并将switchActionManager重置为false:

    public void launchUFO()
    {
        //每次发射之前清0分数
        if (switchActionManager)
        {
            switchActionManager = false;
            myUFOCtrl.switchActionManager();
        }
        myStatusCtrl.resetScore();
        myUFOCtrl.launchUFO();
    }
物理运动管理器类

定义物理运动管理器,主要实现给游戏对象添加一个xyz轴方向上都是在一定数值范围内随机生成的瞬时力,代码如下:

    public class PhysisActionManager
    {
        private SceneController scene;
        private static PhysisActionManager instance;

        private PhysisActionManager()
        {
            scene = SceneController.getInstance();
        }

        public static PhysisActionManager getInstance()
        {
            if (instance == null) instance = new PhysisActionManager();
            return instance;
        }

        private Vector3 getRandomForce()
        {
            int x = UnityEngine.Random.Range(-30, 31);
            int y = UnityEngine.Random.Range(30, 41);
            int z = UnityEngine.Random.Range(20, 31);

            float t = 0.7f + scene.getTrailNum() / 20;
            return new Vector3(x, y, z) * t;
        }

        public void addForce(GameObject UFO)
        {
            Vector3 force = getRandomForce();
            UFO.GetComponent<Rigidbody>().useGravity = true;
            UFO.GetComponent<Rigidbody>().AddForce(force, ForceMode.Impulse);
        }

    }
运动学运动管理器

运动学管理器模式下的物体不使用重力属性,即把游戏对象的刚体属性useGravity设为false,并且给刚体一个初速度使其沿某个方向运动,代码如下:

    public class CCActionManager
    {
        private SceneController scene;
        private static CCActionManager instance;

        private CCActionManager()
        {
            scene = SceneController.getInstance();
        }

        public static CCActionManager getInstance()
        {
            if (instance == null) instance = new CCActionManager();
            return instance;
        }

        public void addForce(GameObject UFO)
        {
            // 控制飞碟向某个随机方向持续移动
            UFO.GetComponent<Rigidbody>().useGravity = false;
            UFO.GetComponent<Rigidbody>().velocity = new Vector3(5, 15, 5);

        }
    }
飞碟控制类

飞碟控制类UFOController中需要定义一个适配器成员对象,用来在launchUFOs(roundNum)函数里(发射飞碟时)调用不同模式下的addForceOnUFO(GameObject)方法。修改后的UFOController类如下:

using CarolSum.com;
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class UFOController : MonoBehaviour {
    //预设文件
    public GameObject PlaneItem, LauncherItem, ExplosionItem;
    public Material greenMat, redMat, blueMat;

    private GameObject plane, launcher, explosion;
    private SceneController scene;
    private UFOActionAdapter adapter;

    //发射每一个飞碟的时间间隔
    private const float LAUNCH_GAP = 0.1f;

	// Use this for initialization
	void Start () {
        scene = SceneController.getInstance();
        scene.setUFOController(this);
        plane = Instantiate(PlaneItem);
        launcher = Instantiate(LauncherItem);
        explosion = Instantiate(ExplosionItem);
        adapter = new UFOActionAdapter();
	}
	
	// Update is called once per frame
	void Update () {
        UFOFactory.getInstance().detectLandingUFOs();
	}

    public void launchUFO()
    {
        int trailNum = scene.getTrailNum() > 3 ? 3 : scene.getTrailNum();
        Debug.Log("发射!");
        if (!UFOFactory.getInstance().isLaunching())
        {
            StartCoroutine(launchUFOs(trailNum));
        }
    }

    IEnumerator launchUFOs(int roundNum)
    {
        for(int i = 0; i<roundNum; i++)
        {
            GameObject UFO = UFOFactory.getInstance().getUFO();
            UFO.transform.position = launcher.transform.position;
            UFO.GetComponent<MeshRenderer>().material = getMaterial(scene.getTrailNum());

            adapter.addForceOnUFO(UFO);

            //每隔LAUNCH_GAP时间发射一个UFO
            yield return new WaitForSeconds(LAUNCH_GAP);
        }
    }

    public void switchActionManager()
    {
        adapter.switchActionMode();
    }

    public void hitUFO(Vector3 mousePos)
    {
        Ray ray = Camera.main.ScreenPointToRay(mousePos);
        RaycastHit hit;
        if(Physics.Raycast(ray, out hit))
        {
            if (hit.collider.gameObject.tag.Equals("UFO"))
            {
                createExplosion(hit.collider.gameObject.transform.position);
                scene.addScore();
                UFOFactory.getInstance().RecyclingUFO(hit.collider.gameObject);
            }
        }
    }

    private void createExplosion(Vector3 position)
    {
        explosion.transform.position = position;
        explosion.GetComponent<ParticleSystem>().GetComponent<Renderer>().material = getMaterial(scene.getTrailNum());
        explosion.GetComponent<ParticleSystem>().Play();
    }

    private Material getMaterial(int roundNum)
    {
        switch(roundNum % 3)
        {
            case 0:
                return redMat;
            case 1:
                return greenMat;
            case 2:
                return blueMat;
            default:
                return redMat; 
        }
    }

}

游戏各预制的属性设置如下:
Canvas:Canvas
Explosion:
Explosion
Launcher:
Launcher
Plane:
Plane
UFO:
UFO
Main Camera挂载
MainCamera
Empty对象挂载
Empty

最终运行效果如下:
Disk_improved运行录屏

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值