改进飞碟(Hit UFO)游戏

基本介绍


游戏内容要求

  1. 靶对象为 5 环,按环计分
  2. 箭对象,射中后要插在靶上
增强要求:射中后,箭对象产生颤抖效果,到下一次射击 或 1秒以后
  1. 游戏仅一轮,无限 trials;
增强要求:添加一个风向和强度标志,提高难度

实现


UML

这个设计和打靶游戏的是一致的,主要是具体动作、控制器等具体实现有些是不同的。

  • 靶子是由5个圆柱体组成,每个圆柱体上添加一个Mesh Collider组件,勾选isTrigger做触发器使用。
  • 每个圆柱体上挂载了CollisionDetection和RingData脚本。RingData脚本上只有一个分数属性,代表了这一环的分数。

最后预制体图如下:

unity pun游戏_ide


unity pun游戏_单实例_02

弓和箭

使用的是资源LowpolyWeaponPack中的预制:

unity pun游戏_ide_03


unity pun游戏_unity pun游戏_04

代码

部分代码都是沿用的打飞碟游戏的,就不一一展示了,就说明一下重点部分咯

  • FirstSenceController

Update函数,使用LookAt函数将弓箭转向鼠标的方向,如果没有箭则从工厂中取出一支箭放在弓上,检测鼠标左键按下则射箭。

void Update () {
        if(game_start) {
            Vector3 mpos = Camera.main.ScreenPointToRay(Input.mousePosition).direction;
            if (Input.GetButtonDown("Fire1")) {
                Shoot(mpos * 15 );
            }
            if (arrow == null) {
                arrow = arrow_factory.GetArrow();
                arrow.transform.position = bow.transform.position;
                arrow.gameObject.SetActive(true);
                arrow.GetComponent<Rigidbody>().isKinematic = true;
            }
            bow.transform.LookAt(mpos * 30);
            arrow.transform.LookAt(mpos * 30);
            arrow_factory.FreeArrow();
        }
    }

Shoot函数:通知动作管理器根据初始的力和风力射箭,并且设置新的更强的风向。

public void Shoot(Vector3 force) {
        if (arrow != null) {
            arrow.GetComponent<Rigidbody>().isKinematic = false;
            action_manager.ArrowFly(arrow, wind, force);
            child_camera.GetComponent<ChildCamera>().StartShow();
            arrow = null;
            CreateWind();
            round++;
        }
    }
  • ArrowFlyAction

与打飞碟相同,实现箭飞行的动作,给一个冲击力和风力即可。

public class ArrowFlyAction : SSAction {
    public Vector3 _force;
    public Vector3 _wind;

    private ArrowFlyAction() { }

    public static ArrowFlyAction GetSSAction(Vector3 wind, Vector3 force) {
        ArrowFlyAction action = CreateInstance<ArrowFlyAction>();
        action._force = force;
        action._wind = wind;
        return action;
    }
    
    public override void Start() {
        gameobject.GetComponent<Rigidbody>().AddForce(_force, ForceMode.Impulse);
        gameobject.GetComponent<Rigidbody>().AddForce(_wind);
    }

    public override void Update() {}

    public override void FixedUpdate(){
        if (transform.position.y < -30) {
            this.destroy = true;
            this.callback.SSActionEvent(this);
        }
    }
}
  • ArrowFlyActionManager
public class ArrowFlyActionManager : SSActionManager {
    private ArrowFlyAction fly; 
    public FirstSceneController scene_controller; 
    
    protected void Start() {
        scene_controller = (FirstSceneController)SSDirector.GetInstance().CurrentScenceController;
        scene_controller.action_manager = this;
    }

    public void ArrowFly(GameObject arrow, Vector3 wind, Vector3 force) {
        fly = ArrowFlyAction.GetSSAction(wind, force);
        this.RunAction(arrow, fly, this);
    }
}
  • SSAction
    动作基类
  • SSActionManager
    动作管理基类
  • CollisionDetection

检测碰撞,将其挂在靶子的每一环上,使用OnTriggerEnter函数检测碰撞,碰撞之后将箭头隐藏并且将箭设置为运动学停止其运动。

public class CollisionDetection : MonoBehaviour {
    public FirstSceneController scene_controller;
    public ScoreRecorder recorder;

    void Start() {
        scene_controller = SSDirector.GetInstance().CurrentScenceController as FirstSceneController;
        recorder = Singleton<ScoreRecorder>.Instance;
    }

    void OnTriggerEnter(Collider arrow_head) {
        Transform arrow = arrow_head.gameObject.transform.parent;
        if (arrow == null) return;
        arrow.GetComponent<Rigidbody>().isKinematic = true;
        arrow_head.gameObject.SetActive(false);
        recorder.Record(this.gameObject);
    }
}
  • ChildCamera
    在设定的时间内,显示副相机,可以让用户看清楚射出去箭在靶上的位置,挂载在副相机上。(初始设定副相机不显示)
public class ChildCamera : MonoBehaviour
{   
    public bool isShow = false;                   //是否显示副摄像机
    public float leftTime;                        //显示时间

    void Update()
    {
        if (isShow)
        {
            leftTime -= Time.deltaTime;
            if (leftTime <= 0)
            {
                this.gameObject.SetActive(false);
                isShow = false;
            }
        }
    }

    public void StartShow()
    {
        this.gameObject.SetActive(true);
        isShow = true;
        leftTime = 2f;
    }
}
  • interface
    接口类
  • ArrowFactory
    工厂类
  • RingData
    射击环得分
  • ScoreRecorder
    记录得分
  • UserGUI
    用户界面设计
  • Singleton
    单实例
  • SSDirector

游戏运行结果


unity pun游戏_碰撞检测_05

后记


完整代码

这个项目主要是碰撞问题,之前出现了箭一下子穿过去了的问题,然后看到了一个博客,记录一下。

当物体运动速度比较快或者碰撞体比较薄时,可能会出现穿过去而没有出发碰撞的情况,此时需要将速度快的物体即箭碰撞检测调为持续动态,将不动的物体碰撞检测设为持续。