Unity 打靶游戏实践

最终实现效果:

打靶游戏

一、实现思路

改编自之前的飞碟工厂,在弓弩上没有弓箭时重新生成新的弓箭,并将射出的弓箭在一定时间后进行回收(下落到一定地方)。在右下角通过小窗口展示靶子的情况,射中不同的环数给予不同得分。

二、主要涉及技术

物理引擎的使用、游戏对象的生产与回收、物体碰撞、刚体编程、动画编程、地形制作

三、图形设计

(一)靶子的实现

通过不同大小但是对称轴重合的圆柱体实现靶子。需要注意的是:当圆柱体的高完全相同时,外面更大的圆柱体会覆盖里面更小的圆柱体。所以需要让多个圆柱体从里往外高逐渐递减。


另外还有一些碰撞相关的设置:将根组件设为 Rigidbody ,其他子组件设置为 Mesh Renderer。

(二)弓箭

弓箭是引用Unity资源商店下载的Classical Crossbow。

(三)加入天空特效

用Skybox中的天空盒在window属性进行设置并将相机添加skybox。后面在FirstSceneController.cs中的代码可以实现通过按下按钮而改变天空盒。

(四)加入地形

创建一个terrain,用inspector中的paint terrain画地形,导入资源包,并在上面放置若干树。

四、原理实现

1. FirstController.js
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FirstSceneController : MonoBehaviour, IUserAction, ISceneController { 
    public Camera child_camera;
    public ScoreRecorder recorder;
    public ArrowFactory arrow_factory;
    public ArrowFlyActionManager action_manager;

    public Material skybox1;
    public Material skybox2;
    public int weather = 1;

    public GameObject bow;
    private GameObject arrow;                   
    private GameObject target;
    private GameObject Terrain;
    private int round = 0;

    private float downtime = 0;

    private bool game_over = false;
    private bool game_start = false; 
    private string wind_name = "";  
    private Vector3 wind = new Vector3(0, 0, 0);
    private Vector3 force;

    void Start () {
        SSDirector director = SSDirector.GetInstance();
        arrow_factory = Singleton<ArrowFactory>.Instance;
        recorder = Singleton<ScoreRecorder>.Instance;
        director.CurrentScenceController = this;
        action_manager = gameObject.AddComponent<ArrowFlyActionManager>() as ArrowFlyActionManager;
        LoadResources();
        CreateWind();
    }

    void Update () {
        if(game_start) {
            Vector3 mpos = Camera.main.ScreenPointToRay(Input.mousePosition).direction;
            if(Input.GetButton("Fire1"))
            {
                downtime += Time.deltaTime;
            }
            if (Input.GetButtonUp("Fire1") && downtime < 0.4)
            {
                downtime = 0;
            }
            if (Input.GetButtonUp("Fire1") && downtime >0.4) {
                Shoot(mpos * 15 );
                downtime = 0;
            }
            if (Input.GetButtonDown("Fire2"))
            {
                ChangeWeather();
            }
            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();
        }
    }

    
    public void ChangeWeather()
    {
        int j = -1;
        weather *= j;
        if(weather == 1)
        {
            skybox1 = Instantiate(Resources.Load("Materials/Skybox_Day", typeof(Material))) as Material;
            RenderSettings.skybox = skybox1;
        }
        if(weather == -1)
        {
            skybox2 = Instantiate(Resources.Load("Materials/Skybox_Set", typeof(Material))) as Material;
            RenderSettings.skybox = skybox2;
        }
    }

    public void LoadResources() {
        bow = Instantiate(Resources.Load("Prefabs/bow", typeof(GameObject))) as GameObject;
        target = Instantiate(Resources.Load("Prefabs/target", typeof(GameObject))) as GameObject;
        Terrain = Instantiate(Resources.Load("Prefabs/Terrain", typeof(GameObject))) as GameObject;
    }
    
    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++;
        }
    }

    public int GetScore() {
        return recorder.score;
    }

    public bool GetGameover() {
        return game_over;
    }

    public string GetWind() {
        return wind_name;
    }

    public void CreateWind() {
        float wind_directX = ((Random.Range(-10, 10) > 0) ? 1 : -1) * round;
        float wind_directY = ((Random.Range(-10, 10) > 0) ? 1 : -1) * round;
        Debug.Log(wind_directX);
        wind = new Vector3(wind_directX, wind_directY, 0);

        string Horizontal = "", Vertical = "", level = "";
        if (wind_directX > 0) {
            Horizontal = "西";
        } else if (wind_directX <= 0) {
            Horizontal = "东";
        }
        if (wind_directY > 0) {
            Vertical = "南";
        } else if (wind_directY <= 0) {
            Vertical = "北";
        }
        level = round.ToString();
        wind_name = Horizontal + Vertical + "风" + " " + level;
    }

    public void BeginGame() {
        Cursor.visible = false;
        game_start = true;
    }
}

2.UserGUI

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

public class UserGUI : MonoBehaviour {
    private IUserAction action;
    GUIStyle score_style = new GUIStyle();
    GUIStyle text_style = new GUIStyle();
    private bool game_start = false;

    void Start () {
        action = SSDirector.GetInstance().CurrentScenceController as IUserAction;
        text_style.normal.textColor = new Color(0, 0, 0, 1);
        text_style.fontSize = 16;
        score_style.normal.textColor = new Color(1, 0, 0, 1);
        score_style.fontSize = 16;
    }

    void Update() {}
    private void OnGUI() {
        if(game_start) {
            GUI.Label(new Rect(15, 10, 205, 55), "分数:", text_style);
            GUI.Label(new Rect(60, 10, 205, 55), action.GetScore().ToString(), score_style);
            GUI.Label(new Rect(15, 55, 205, 110), "按下鼠标右键切换天气!", text_style);
            GUI.Label(new Rect(Screen.width - 170, 30, 200, 50), "风向: ", text_style);
            GUI.Label(new Rect(Screen.width - 110, 30, 200, 50), action.GetWind(), text_style);
        }
        else {
            GUI.Label(new Rect(Screen.width / 2 - 30, Screen.width / 2 - 320, 100, 100), "打靶游戏", text_style);
            if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.width / 2 - 150, 100, 50), "游戏开始")) {
                game_start = true;
                action.BeginGame();
            }
        }
    }
}

3.动画animator

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

public class Animator : MonoBehaviour
{
    Animator animator;
    public int startint;
    private float downtime = 0;
    // Start is called before the first frame update
    void Start()
    {
        animator = GetComponent<Animator>();
        startint = 1;
    }

    // Update is called once per frame
    void Update()
    {
            
            if (Input.GetButton("Fire1") && startint == 1)
            {
                startint = 0;
            }
            if (Input.GetButton("Fire1") && startint == 0)
            {
                downtime += Time.deltaTime;
            }
            if(downtime > 0.15)
            {
                animator.SetBool("Fire", true);
            }
            if (Input.GetButtonUp("Fire1"))
            {
                animator.SetBool("Fire", false);
                downtime = 0;   
            }
               
       
    }
}

4.Arrow Factory

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

//工厂模式,箭工厂
public class ArrowFactory : MonoBehaviour {
    public GameObject arrow = null;                             
    private List<GameObject> used = new List<GameObject>();   
    private Queue<GameObject> free = new Queue<GameObject>();  
    public FirstSceneController sceneController;

    public void Start() {
        sceneController = (FirstSceneController)SSDirector.GetInstance().CurrentScenceController;
    }
    public GameObject GetArrow() {
        if (free.Count == 0) {
            arrow = Instantiate(Resources.Load<GameObject>("Prefabs/arrow"));
        } else {
            arrow = free.Dequeue();
            arrow.gameObject.SetActive(true);
        }
        used.Add(arrow);
        return arrow;
    }

    public void FreeArrow() {
        for (int i = 0; i < used.Count; i++) {
            if (used[i].gameObject.transform.position.y < -30) {
                used[i].gameObject.SetActive(false);
                free.Enqueue(used[i]);
                used.Remove(used[i]);
                break;
            }
        }
    }
}

5.ArrowFlyAction

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

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);
        }
    }
}

6.Collision Detection 碰撞检测

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

//碰撞检测器,检测碰撞
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);
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值