物体碰撞和检测——Hit UFO改进版&射箭游戏

Hit UFO改进版

一、改进要求

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

二、MVC图

在这里插入图片描述

三、gitee

gitee

四、程序实现

本次作业使用上一次作业Hit UFO来进行改进,主要实现部分为增加了适配器和物理模式的切换,其余部分几乎没有改变,详情见上一次博客 hit ufo 基础版

4.1 FirstController

沿用之前的代码,只不过增加了一个用户操作changeStyle,在其中要调用飞碟工厂类中的rmgrivity函数来取消调刚体重力,和刚体速度

    public void changeStyle()
    {
        if (isPhy)
        {
            disk_factory.rmgrivity();
        }
        isPhy = !isPhy;
    }

4.2 Interface

增加了用户操作changeStyle

public interface IUserAction                              
{
    void Hit(Vector3 pos);
    int GetScore();
    void GameOver();
    void ReStart();
    void BeginGame();
    void changeStyle();
}

4.3 ActionManagerAdapter

作为插入到原有的动作管理类上层的一个文件,决定着调用哪一种动作完成请求

public class ActionManagerAdapter : MonoBehaviour, IActionManager
{
    public FlyActionManager action_manager;
    public PhysisFlyActionManager phy_action_manager;
    public void playDisk(GameObject disk, float angle, float power, bool isPhy)
    {
        Debug.Log("phy is : !" + isPhy);
        if (isPhy)
        {
            Debug.Log("phy");
            //disk.AddComponent<>
            phy_action_manager.UFOFly(disk, angle, power);
        }
        else
        {
            Debug.Log("not phy");
            action_manager.UFOFly(disk, angle, power);
        }
    }
    // Use this for initialization
    void Start()
    {
        action_manager = gameObject.AddComponent<FlyActionManager>() as FlyActionManager;
        phy_action_manager = gameObject.AddComponent<PhysisFlyActionManager>() as PhysisFlyActionManager;
    }

}

4.4 PhsisFlyActionManager

物理动作管理类,用于管理物理动作

public class PhysisFlyActionManager : SSActionManager
{

    public PhysisUFOFlyAction fly;                            //飞碟飞行的动作
    //public FirstController scene_controller;             //当前场景的场景控制器

    protected void Start()
    {
    }
    //飞碟飞行
    public void UFOFly(GameObject disk, float angle, float power)
    {
        fly = PhysisUFOFlyAction.GetSSAction(disk.GetComponent<DiskData>().direction, angle, power);
        this.RunAction(disk, fly, this);
    }
}

4.5 PhysisUFOFlyAction

设定飞碟物理模式下的运动,这里通过添加重力以及velocity实现

public class PhysisUFOFlyAction : SSAction
{
    private Vector3 start_vector;                              //初速度向量
    public float power;
    private PhysisUFOFlyAction() { }
    public static PhysisUFOFlyAction GetSSAction(Vector3 direction, float angle, float power)
    {
        //初始化物体将要运动的初速度向量
        PhysisUFOFlyAction action = CreateInstance<PhysisUFOFlyAction>();
        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;
        }
        action.power = power;
        return action;
    }

    public override void FixedUpdate()
    {

        //判断是否超出范围
        if (this.transform.position.y < -10)
        {
            this.destroy = true;
            this.callback.SSActionEvent(this);
        }
    }
    public override void Update() { }
    public override void Start()
    {
        //使用重力以及给一个初速度
        gameobject.GetComponent<Rigidbody>().velocity = power / 20 * start_vector;
        gameobject.GetComponent<Rigidbody>().useGravity = true;
    }
}

4.5 DiskFactory

工厂类中增加了rmgrivity函数,用于从物理模式切换到运动学模式时,消除物理学的刚体组件中的参数

    public void rmgrivity()
    {
        for (int i = 0; i < used.Count; i++)
        {
            used[i].GetComponent<Rigidbody>().useGravity = false;
            used[i].GetComponent<Rigidbody>().velocity = 0*Vector3.zero;
        }
        for (int i = 0; i < free.Count; i++)
        {
            free[i].GetComponent<Rigidbody>().useGravity = false;
            free[i].GetComponent<Rigidbody>().velocity = 0 * Vector3.zero;
        }
    }

五、效果展示

在这里插入图片描述

  • 物理模式:

在这里插入图片描述

  • 运动学模式:

在这里插入图片描述

射箭游戏

一、实现功能:

  • 靶对象为 5 环,按环计分;
  • 箭对象,射中后要插在靶上
    • 射中后,箭对象产生颤抖效果,持续八秒
  • 游戏仅一轮,无限 trials
    • 添加一个风向和强度标志,提高难度
  • 模拟真实射箭过程
    • 按住右键调整弓箭方向
    • 按住左键进行蓄力,松开后发射,根据蓄力时间决定力度

二、程序实现:

2.1 SceneController & interface

首先定义用户动作,并在SceneController中定义动作调用

namespace Scene
{
    public interface IUserAction
    {
        void getArrow();
        void shootArrow(Vector3 mousePos,float xuli);
        void changeArrowpos(Vector3 mousePos);
    }

    public interface ISceneController
    {
        void addScore(int point);
        //void showTips(int point);
        void changeWind();
        int getWindDirection();
        int getWindStrength();
        bool ifReadyToShoot();
    }

    public class SceneController : System.Object, IUserAction, ISceneController
    {
        private static SceneController instance;
        private GameStateController gameStateController;
        private ArrowController arrowController;

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

        public void setGameController(GameStateController gsc)
        {
            gameStateController = gsc;
        }

        public void setArrowController(ArrowController _arrowController)
        {
            arrowController = _arrowController;
        }

        public void addScore(int point)
        {
            gameStateController.addScore(point);
        }

        public void changeWind()
        {
            gameStateController.changeWind();
        }

        public void getArrow()
        {
            arrowController.getArrow();
        }

        public int getWindDirection()
        {
            return gameStateController.getWindDirec();
        }

        public int getWindStrength()
        {
            return gameStateController.getWindStrength();
        }

        public bool ifReadyToShoot()
        {
            return arrowController.ifReadyToShoot();
        }

        public void shootArrow(Vector3 mousePos,float xuli)
        {
            arrowController.shootArrow(mousePos,xuli);
        }
        public void changeArrowpos(Vector3 mousePos)
        {
            arrowController.changeArrowpos(mousePos);
        }

    }

2.2 GameStateController

这里完成积分工作和风向提示,在OnGUI中更新数据

public class GameStateController : MonoBehaviour {
    public int i = 0;
    private int score = 0, windDir = 0, windStrength = 0;

    GUIStyle bold_style = new GUIStyle();
    GUIStyle score_style = new GUIStyle();
    GUIStyle text_style = new GUIStyle();
    GUIStyle over_style = new GUIStyle();
    private int high_score = 0;

    //private const float TIPS_SHOW_TIME = 0.5f;

    //private GameObject canvas, scoreText, tipsText, windText;
    private SceneController scene;
    private string[] windDirectionArray;

    // Use this for initialization
    void Start () {
        scene = SceneController.getInstance();
        scene.setGameController(this);
        windDirectionArray = new string[8] { "↑", "↗", "→", "↘", "↓", "↙", "←", "↖" };
        changeWind();
    }

    public void changeWind()
    {
        windDir = UnityEngine.Random.Range(0,8);
        windStrength = UnityEngine.Random.Range(0, 8);
    }

    internal void addScore(int point)
    {
        i++;
        if (i % 2 == 0)
            high_score += point;
        changeWind();
    }

    // Update is called once per frame
    void Update () {
		
	}

    void OnGUI()
    {
        bold_style.normal.textColor = new Color(1, 0, 0);
        bold_style.fontSize = 16;
        text_style.normal.textColor = new Color(0, 0, 0, 1);
        text_style.fontSize = 16;
        score_style.normal.textColor = new Color(1, 0, 1, 1);
        score_style.fontSize = 16;
        over_style.normal.textColor = new Color(1, 0, 0);
        over_style.fontSize = 25;

        GUI.Label(new Rect(10, 5, 200, 50), "分数:" +high_score, text_style);
        GUI.Label(new Rect(10, 105, 200, 50), "Wind: " + windDirectionArray[windDir] + " x" + windStrength, text_style);
    }

    internal int getWindDirec()
    {
        return windDir;
    }

    internal int getWindStrength()
    {
        return windStrength;
    }

}

2.3 UserInterface

检测鼠标的动作,这里定义了一个时间用于判断蓄力,并检测鼠标左右键的按下和松开,来进行角度调整和拉弓射箭,模拟真实射箭环境

public class UserInterface : MonoBehaviour {

    private IUserAction action;
    private ISceneController scene;
    bool gameStart = false;
    int timeCount = 0;
    bool left = false;
    bool right = false;
    bool timeflag;
    float xuli;

    // Use this for initialization
    void Start () {
        action = SceneController.getInstance() as IUserAction;
        scene = SceneController.getInstance() as ISceneController;
	}
	
	void Update () {
        if (!gameStart)
        {
            action.getArrow();
            gameStart = true;
        }

        if (timeCount%75==0) action.getArrow();


        if (Input.GetMouseButtonUp(1))
        {
            right = false;
        }
        if (scene.ifReadyToShoot())
        {
            //
            if (Input.GetMouseButtonDown(1))
            {
                right = true;
            }

            if (right)
            {
                Vector3 mousePos = Camera.main.ScreenPointToRay(Input.mousePosition).direction;
                action.changeArrowpos(mousePos);
            }
            if (Input.GetMouseButtonDown(0))
            {
                timeflag = true;
                xuli = 0;
            }
            //左键松开,射出弓箭
            if (Input.GetMouseButtonUp(0))
            {
                Vector3 mousePos = Camera.main.ScreenPointToRay(Input.mousePosition).direction;
                action.shootArrow(mousePos,xuli);
                timeCount = 0;
                timeflag = false;
            }
            if (timeflag)
            {
                xuli += Time.deltaTime;
            }
        }
        timeCount++;
	}

}

2.4 ArrowFactory

弓箭的工厂类,定义弓箭的产生和获取

public class ArrowFactory : System.Object
{
    private static ArrowFactory instance;
    private GameObject ArrowPrefabs;
    private List<GameObject> usingArrowList = new List<GameObject>();
    private List<GameObject> freeArrowList = new List<GameObject>();
    private Vector3 INITIAL_POS = new Vector3(0, 0, -19);

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

    public void init(GameObject _arrowPrefabs)
    {
        ArrowPrefabs = _arrowPrefabs;
    }

    internal void detectArrowsReuse()
    {
       for(int i = 0; i < usingArrowList.Count; i++)
        {
            if(usingArrowList[i].transform.position.y <= -8)
            {
                usingArrowList[i].GetComponent<Rigidbody>().isKinematic = true;
                usingArrowList[i].SetActive(false);
                usingArrowList[i].transform.position = INITIAL_POS;
                freeArrowList.Add(usingArrowList[i]);
                usingArrowList.Remove(usingArrowList[i]);
                i--;
                SceneController.getInstance().changeWind();
            }
        } 
    }

    internal GameObject getArrow()
    {
        if(freeArrowList.Count == 0)
        {
            GameObject newArrow = Camera.Instantiate(ArrowPrefabs);
            usingArrowList.Add(newArrow);
            return newArrow;
        }
        else
        {
            GameObject oldArrow = freeArrowList[0];
            freeArrowList.RemoveAt(0);
            oldArrow.SetActive(true);
            usingArrowList.Add(oldArrow);
            return oldArrow;
        }
    }
}

2.5 ArrowCollider

放在箭头上的脚本,用于判断是否碰触到板子,然后为弓箭添加颤抖组件

public class ArrowCollider : MonoBehaviour {
    private ISceneController scene;
    float radian = 0;                             // 弧度  
    float per_radian = 3f;                        // 每次变化的弧度  
    float radius = 0.01f;                         // 半径  
    Vector3 old_pos;                              // 开始时候的坐标  
    public float left_time = 8f;                 //动作持续时间

    void Start () {
        scene = SceneController.getInstance() as ISceneController;
	}

	void Update () {
        
	}

    [System.Obsolete]
    void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.tag == "target")
        {
            gameObject.transform.parent.gameObject.GetComponent<Rigidbody>().isKinematic = true;
            gameObject.SetActive(false);
            int points = 0;
            if(other.gameObject.name.ToString() == "1")
            {
                points = 1;
            }
            if (other.gameObject.name.ToString() == "2")
            {
                points = 2;
            }
            if (other.gameObject.name.ToString() == "3")
            {
                points = 3;
            }
            if (other.gameObject.name.ToString() == "4")
            {
                points = 4;
            }
            if (other.gameObject.name.ToString() == "5")
            {
                points = 5;
            }
            if (other.gameObject.name.ToString() == "6")
            {
                points = 6;
            }
            scene.addScore(points);
            //scene.showTips(points);
            gameObject.transform.parent.FindChild("Body").gameObject.AddComponent<bodytremble>();
            Debug.Log("pengzhuang");
        }
    }
}

2.6 ArrowController

增加风力,并控制弓箭和靶子的产生和选择

public class ArrowController : MonoBehaviour {

    public GameObject TargetPrefabs, ArrowPrefabs;

    private GameObject holdingArrow, target;
    private const int SPEED = 40;
    private SceneController scene;
    private Vector3[] winds = new Vector3[8]
    {
        new Vector3(0, 1, 0), new Vector3(1,1,0), new Vector3(1,0,0), new Vector3(1,-1,0), new Vector3(0,-1,0), new Vector3(-1,-1,0), new Vector3(-1,0,0), new Vector3(-1,1,0)
    };

    private void Awake()
    {
        ArrowFactory.getInstance().init(ArrowPrefabs);
    }

    // Use this for initialization
    void Start () {
        scene = SceneController.getInstance();
        scene.setArrowController(this);
        target = Instantiate(TargetPrefabs);
	}
	
	// Update is called once per frame
	void Update () {
        ArrowFactory.getInstance().detectArrowsReuse();
    }

    public bool ifReadyToShoot()
    {
        return (holdingArrow != null);
    }

    internal void getArrow()
    {
        if (holdingArrow == null) holdingArrow = ArrowFactory.getInstance().getArrow();
    }

    internal void shootArrow(Vector3 mousePos,float xuli)
    {
        holdingArrow.transform.LookAt(mousePos * 30);
        holdingArrow.GetComponent<Rigidbody>().isKinematic = false;
        addWind();  //箭在射出过程中会持续受到一个风力
        if(xuli <= 2)
        {
            holdingArrow.GetComponent<Rigidbody>().AddForce(mousePos * 30 * xuli, ForceMode.Impulse);
        }
        else
        {
            holdingArrow.GetComponent<Rigidbody>().AddForce(mousePos * 60, ForceMode.Impulse);
        }
        
        holdingArrow = null;
    }

    internal void changeArrowpos(Vector3 mousePos)
    {
        holdingArrow.transform.LookAt(mousePos * 30);
    }

    private void addWind()
    {
        int windDir = scene.getWindDirection();
        int windStrength = scene.getWindStrength();
        Vector3 windForce = winds[windDir]*50;
        holdingArrow.GetComponent<Rigidbody>().AddForce(windForce, ForceMode.Force);
    }
}

2.7 bodytremble

颤抖脚本,以原来位置为基础,进行7弧度的颤抖

public class bodytremble : MonoBehaviour
{
    float radian = 0;                             // 弧度  
    float per_radian = 7f;                        // 每次变化的弧度  
    float radius = 0.01f;                         // 半径  
    Vector3 old_pos;                              // 开始时候的坐标  
    public float left_time = 8f;                 //动作持续时间
    // Start is called before the first frame update
    void Start()
    {
        old_pos = transform.position;
    }

    // Update is called once per frame
    void Update()
    {
        left_time -= Time.deltaTime;
        if (left_time <= 0)
        {
            //颤抖后回到初始位置
            transform.position = old_pos;
        }

        // 弧度每次增加
        radian += per_radian;
        //y轴的位置变化,上下颤抖
        float dy = Mathf.Cos(radian) * radius;
        transform.position = old_pos + new Vector3(0, dy, 0);
        Debug.Log("tremble");
    }
}

三、实现效果

  • 左上摄像机用于观察颤抖效果,右上摄像机用于观察中靶情况

在这里插入图片描述

  • 动画演示:右键移动瞄准,左键按住蓄力,松开发射,这里设定了颤抖8秒,所以左上一直在抖,有点鬼畜

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值