Unity3D小游戏 林中射手

一、游戏背景介绍

你是一名经验老道的猎手,而经验来源于平日刻苦的训练。今天你像往常一样来到林中靶场展开固定靶和移动靶射击训练,你相信,百发百中,就在今朝!

二、游戏实现功能

以下全部

三、游戏玩法介绍

1.玩家仅能在白色射箭区域进行射击游戏,在射箭区域外无法进行射击;

2.每次游戏限时30秒,10支箭,倒计时结束或箭矢射完本轮游戏结束;

3.射中标靶后得10分,同时若标靶为移动靶会停止;

4.弓弩移动时会与树木山峦等地形发生碰撞阻挡,请不要尝试强行穿过障碍,否则头晕目眩的感觉可不好受;

5.随着时间流逝游戏内的天色也会变化,享受在无尽日夜射击的快感吧。

(具体按键功能可查看游戏内提示)

四、游戏代码讲解

游戏大体上采用MVC架构制作,以下针对重点对象进行讲解。

1.Crossbow(Player)

游戏中的弓弩可以通过AWSD移动以及通过点击右键控制俯仰旋转,加载了Move和Rotate两个脚本实现对应功能。(弓弩移动存在y轴限制,达到边界时可能减速,需要注意调整俯仰角度)。

Move

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

public class Move : MonoBehaviour
{
    public float speed = 1.5f;//控制移动速度
    public Transform m_transform;

    // Use this for initialization
    void Start()
    {
        m_transform = this.transform;
    }

    // Update is called once per frame
    void Update()
    {
        //向左
        if (Input.GetKey(KeyCode.A))
        {
            if (m_transform.position.y >= 2 && m_transform.position.y <= 3)
                m_transform.Translate(Vector3.left * Time.deltaTime * speed);
            else if (m_transform.position.y < 2)
                m_transform.position = new Vector3(m_transform.position.x, 2, m_transform.position.z);
            else
                m_transform.position = new Vector3(m_transform.position.x, 3, m_transform.position.z);
        }
        //向右
        if (Input.GetKey(KeyCode.D))
        {
            if (m_transform.position.y >= 2 && m_transform.position.y <= 3)
                m_transform.Translate(Vector3.right * Time.deltaTime * speed);
            else if (m_transform.position.y < 2)
                m_transform.position = new Vector3(m_transform.position.x, 2, m_transform.position.z);
            else
                m_transform.position = new Vector3(m_transform.position.x, 3, m_transform.position.z);
        }
        //向前
        if (Input.GetKey(KeyCode.W))
        {
            if (m_transform.position.y >= 2 && m_transform.position.y <= 3)
                m_transform.Translate(Vector3.forward * Time.deltaTime * speed);
            else if (m_transform.position.y < 2)
                m_transform.position = new Vector3(m_transform.position.x, 2, m_transform.position.z);
            else
                m_transform.position = new Vector3(m_transform.position.x, 3, m_transform.position.z);
        }
        //向后
        if (Input.GetKey(KeyCode.S))
        {
            if (m_transform.position.y >= 2 && m_transform.position.y <= 3)
                m_transform.Translate(Vector3.back * Time.deltaTime * speed);
            else if (m_transform.position.y < 2)
                m_transform.position = new Vector3(m_transform.position.x, 2, m_transform.position.z);
            else
                m_transform.position = new Vector3(m_transform.position.x, 3, m_transform.position.z);
        }
    }
}

Rotate

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

public class Rotate : MonoBehaviour
{
    //上下旋转最大角度限制
    public int yMinLimit = -20;
    public int yMaxLimit = 60;
    //旋转速度
    public float xSpeed = 250.0f;//左右旋转速度
    public float ySpeed = 120.0f;//上下旋转速度
    //旋转角度
    private float x = 0.0f;
    private float y = 0.0f;

    void Update()
    {
        if (Input.GetMouseButton(1))
        {
            //Input.GetAxis("MouseX")获取鼠标移动的X轴的距离
            x += Input.GetAxis("Mouse X") * xSpeed * 0.02f;
            y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
            y = ClampAngle(y, yMinLimit, yMaxLimit);
            //欧拉角转化为四元数
            Quaternion rotation = Quaternion.Euler(y, x, 0);
            transform.rotation = rotation;
        }
    }

    //角度范围值限定
    static float ClampAngle(float angle, float min, float max)
    {
        if (angle < -360)
            angle += 360;
        if (angle > 360)
            angle -= 360;
        return Mathf.Clamp(angle, min, max);
    }
}

2.Target

游戏中的标靶分为固定靶和移动靶,移动靶由动画控制移动,本体为自制圆柱组合体。需要注意的是Target加载了自制模块Freeze来控制与箭矢碰撞时的双方冻结,移动靶的移动动画中止和得分累计。

Target

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

public class Target
{
    public GameObject target;
    public int mtype;

    public Target(Vector3 position, int id, int type)
    {
        mtype = type;
        if (type == 0)
            target = GameObject.Instantiate(Resources.Load("Prefabs/target", typeof(GameObject))) as GameObject;
        else if (type == 1)
            target = GameObject.Instantiate(Resources.Load("Prefabs/wandertarget", typeof(GameObject))) as GameObject;
        else
            target = GameObject.Instantiate(Resources.Load("Prefabs/wandertarget2", typeof(GameObject))) as GameObject;
        target.name = "target" + id;
        target.transform.position = position;
        target.AddComponent<Freeze>();
    }
}

TargetCtrl 

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

public class TargetCtrl
{
    Target targetModel;
    public void CreateTarget(Vector3 position, int id, int type)
    {
        if (targetModel == null)
        {
            targetModel = new Target(position, id, type);
        }
    }

    public void DeleteTarget()
    {
        Object.DestroyImmediate(targetModel.target);
    }
}

Freeze 

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

public class Freeze : MonoBehaviour
{
    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("Arr"))
        {
            gameObject.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
            if (gameObject.GetComponent<Animator>() != null)
                gameObject.GetComponent<Animator>().enabled = false;
            collision.gameObject.GetComponent<Rigidbody>().constraints = RigidbodyConstraints.FreezeAll;
            GameObject.FindWithTag("Admin").GetComponent<UserGUI>().score += 10;
        }
    }
}

3.Arrow

游戏中的箭矢模型源自资源商店中的RyuGiKen,自带功能十分完善,这里只添加了赋予发射力的函数Fire。

Arrow 

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

public class Arrow
{
    public GameObject arrow;
    public bool isHit = false;
    public Collider ac;
    public TrailRenderer tr;
    public Rigidbody rb;
    public float time;
    public float power;

    public Arrow(Vector3 position, Quaternion rotation, int id)
    {
        arrow = GameObject.Instantiate(Resources.Load("Prefabs/arrow", typeof(GameObject))) as GameObject;
        arrow.name = "arrow" + id;
        arrow.transform.position = position;
        arrow.transform.rotation = rotation;
        rb = arrow.GetComponent<Rigidbody>();
        ac = arrow.GetComponent<Collider>();
        tr = arrow.GetComponent<TrailRenderer>();
    }
}

ArrowCtrl 

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

public class ArrowCtrl
{
    Arrow arrowModel;
    public void CreateArrow(Vector3 position, Quaternion rotation, int id)
    {
        arrowModel = new Arrow(position, rotation, id);
    }

    public void DeleteArrow()
    {
        Object.DestroyImmediate(arrowModel.arrow);
    }

    public void Fire(float power)
    {
        arrowModel.rb.AddForce(arrowModel.arrow.transform.forward * power * 10);
    }
}

4.ShootArea

直接在指定地点生成平面即可。

ShootArea

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

public class ShootArea
{
    public GameObject shootarea;

    public ShootArea(Vector3 position, int id)
    {
        shootarea = GameObject.Instantiate(Resources.Load("Prefabs/plane", typeof(GameObject))) as GameObject;
        shootarea.name = "shootarea" + id;
        shootarea.transform.position = position;
    }
}

ShootAreaCtrl  

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

public class ShootAreaCtrl
{
    ShootArea shootareaModel;
    public void CreateShootArea(Vector3 position, int id)
    {
        if (shootareaModel == null)
        {
            shootareaModel = new ShootArea(position, id);
        }
    }

    public void DeleteShootArea()
    {
        Object.DestroyImmediate(shootareaModel.shootarea);
    }
}

5.SkyBox

创建空对象挂载脚本,添加天空盒轮换元素,每10秒一次变换。

SkyBoxChange

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

public class SkyBoxChange : MonoBehaviour
{
    public Material[] skyboxMaterials;
    public int index = 0;
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(ChangeSkybox());
    }

    IEnumerator ChangeSkybox()
    {
        while (true)
        {
            // 选择一个天空盒子材质
            int tmp = (index + 1) % 3;
            index = tmp;
            RenderSettings.skybox = skyboxMaterials[tmp];

            // 等待一段时间后再切换天空盒子
            yield return new WaitForSeconds(10);
        }
    }
}

 6.FirstCtrl

总控制器,完成大部分资源生成与控制,管理射击过程。

FirstCtrl

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

public class FirstCtrl : MonoBehaviour
{
    public TargetCtrl[] targetCtrls;
    public Vector3[] targetLocation;
    public ShootAreaCtrl[] SACtrls;
    public Vector3[] SALocation;
    public ArrowCtrl[] arrowCtrls;
    public UserGUI uGUI;
    public bool countdown;
    public bool shooting;
    public float powering;
    public Animator SAanimator;

    void Awake()
    {
        targetLocation = new Vector3[] { new Vector3(0, 5, 5), new Vector3(10, 5, 5), new Vector3(5, 5, 10) , new Vector3(-135, 5, -33) , new Vector3(-140, 5, -40) };
        targetCtrls = new TargetCtrl[5];
        for(int k = 0; k < 3; k++)
        {
            targetCtrls[k] = new TargetCtrl();
            targetCtrls[k].CreateTarget(targetLocation[k], k + 1, 0);
        }
        targetCtrls[3] = new TargetCtrl();
        targetCtrls[3].CreateTarget(targetLocation[3], 4, 1);
        targetCtrls[4] = new TargetCtrl();
        targetCtrls[4].CreateTarget(targetLocation[4], 5, 2);
        SALocation = new Vector3[] { new Vector3(-120, 0.5f, -50), new Vector3(4, 0.5f, -8) };
        SACtrls = new ShootAreaCtrl[2];
        for (int k = 0; k < 2; k++)
        {
            SACtrls[k] = new ShootAreaCtrl();
            SACtrls[k].CreateShootArea(SALocation[k], k + 1);
        }
        arrowCtrls = new ArrowCtrl[11];
        gameObject.AddComponent<UserGUI>();
        uGUI = gameObject.GetComponent<UserGUI>();
        SAanimator = GameObject.FindWithTag("Player").GetComponent<Animator>();
        countdown = false;
        shooting = false;
        powering = 0;
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.G) && uGUI.Sper)
        {
            countdown = true;
            uGUI.time = 30;
            uGUI.score = 0;
            uGUI.arrow = 10;
            GameObject[] SLiving = GameObject.FindGameObjectsWithTag("Arr");
            foreach (GameObject gameObject in SLiving)
            {
                Destroy(gameObject, 0);
            }
            GameObject[] Targets = GameObject.FindGameObjectsWithTag("Tar");
            foreach (GameObject gameObject in Targets)
            {
                if (gameObject.GetComponent<Animator>() != null)
                    gameObject.GetComponent<Animator>().enabled = true;
            }
        }
        if (Input.GetKey(KeyCode.T))
        {
            uGUI.showTips = true;
        }
        if (Input.GetKey(KeyCode.Y))
        {
            uGUI.showTips = false;
        }
        if (countdown)
        {
            if (uGUI.time > 0 && uGUI.Sper)
                uGUI.time -= Time.deltaTime;
            else
                countdown = false; 
            if (Input.GetKeyDown(KeyCode.Mouse0) && uGUI.arrow > 0 && !shooting)
            {
                powering = 0;
                SAanimator.SetTrigger("Holding");
                uGUI.arrow--;
                shooting = true;
            }
            if (Input.GetKey(KeyCode.Mouse0) && shooting)
            {
                powering += Time.deltaTime;
                SAanimator.SetFloat("powering", powering);
            }
            if (Input.GetKey(KeyCode.R) && shooting)
            {
                powering = 0;
            }
            if (Input.GetKey(KeyCode.F) && shooting)
            {
                arrowCtrls[uGUI.arrow] = new ArrowCtrl();
                arrowCtrls[uGUI.arrow].CreateArrow(GameObject.FindWithTag("Player").transform.position, GameObject.FindWithTag("Player").transform.rotation, uGUI.arrow);
                if (powering <= 1)
                    arrowCtrls[uGUI.arrow].Fire(powering);
                else
                    arrowCtrls[uGUI.arrow].Fire(1);
                SAanimator.SetTrigger("Fire");
                shooting = false;
            }
        }
    }
}

 7.UserGUI

较为粗糙的UI,起提示作用。

UserGUI

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

public class UserGUI : MonoBehaviour
{
    public GUIStyle bigStyle,leftStyle;//自定义字体格式
    public string gameMessage;
    public bool Sper;
    public int score;
    public float time;
    public int arrow;
    public bool showTips;
    // Start is called before the first frame update
    void Start()
    {
        gameMessage = "";
        //大字体初始化
        bigStyle = new GUIStyle();
        bigStyle.normal.textColor = Color.red;
        bigStyle.normal.background = null;
        bigStyle.fontSize = 50;
        bigStyle.alignment = TextAnchor.MiddleCenter;
        //左字体初始化
        leftStyle = new GUIStyle();
        leftStyle.normal.textColor = Color.red;
        leftStyle.normal.background = null;
        leftStyle.fontSize = 50;
        leftStyle.alignment = TextAnchor.MiddleLeft;
        Sper = false;
        score = 0;
        time = 30;
        arrow = 10;
    }

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

    void OnGUI()
    {
        Vector3 vec3 = GameObject.FindWithTag("Player").transform.position;
        if (vec3.x > -11 && vec3.x < 19 && vec3.z > -10.5f && vec3.z < -6.5f) // 4 -8
        {
            gameMessage = "处于【固定靶射击】区域内";
            Sper = true;
        }
        else if (vec3.x > -135 && vec3.x < -105 && vec3.z > -52.5f && vec3.z < -48.5f) // -120 -50
        {
            gameMessage = "处于【移动靶射击】区域内";
            Sper = true;
        }
        else
        {
            gameMessage = "不处于游戏区域内";
            Sper = false;
            arrow = 10;
            time = 30;
        }
        GUI.Label(new Rect(0, 50, 700, 50), gameMessage, bigStyle);
        if (Sper)
        {
            GUI.Label(new Rect(10, 100, 300, 100), "Score: " + score, leftStyle);
            GUI.Label(new Rect(10, 150, 350, 150), "Time: " + (int)time + " / 30s", leftStyle);
            GUI.Label(new Rect(10, 200, 350, 200), "Arrow: " + arrow + " / 10", leftStyle);
            GUI.Label(new Rect(10, 250, 350, 250), "按T打开提示", leftStyle);
        }
        if (showTips)
        {
            GUI.Box(new Rect(50, 50, 1050, 1050), "游戏说明\n" +
                "1.到达射箭区后按G开始/重置游戏\n" +
                "2.按左键开始蓄力,按R重置蓄力,按F开火\n" +
                "3.按AWSD移动弓弩,按右键控制旋转以及俯仰\n" +
                "4.按Y关闭提示", leftStyle);
        }
    }
}

五、实机演示

Bilibili: Unity3D小游戏 林中射手_哔哩哔哩bilibili_第一人称

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值