Unity3D项目:射击打靶

 游戏设计

  • 游戏内容要求
    •  地形:使用地形组件,上面有草、树;
    •   天空盒:使用天空盒,天空可随玩家位置 或 时间变化 或 按特定按键切换天空盒;
    •   固定靶:有一个以上固定的靶标;
    •   运动靶:有一个以上运动靶标,运动轨迹,速度使用动画控制;
    •   射击位:地图上应标记若干射击位,仅在射击位附近可以拉弓射击,每个位置有 n 次机会;
    •  驽弓动画:支持蓄力半拉弓,然后 hold,择机 shoot;
    •  游走:玩家的驽弓可在地图上游走,不能碰上树和靶标等障碍;
    •  碰撞与计分:在射击位,射中靶标的相应分数,规则自定; 
  • 玩家动作表(游戏规则表):
动作条件结果
WASD玩家在地形组件上角色移动
角色移动角色碰撞到树木、靶子角色受到阻碍后停止移动
时间变化天空盒和光照s
按下鼠标左键角色在射击位置上,允许射击,箭矢数目大于0弩箭开始蓄力
长按鼠标左键弩箭准备蓄力拉力不断加大直到达到上限
松开鼠标左键弩箭射击
按下鼠标右键并拖动角色视角拖动
箭矢涉及到靶子上依据射击到的靶子颜色,添加相应的分数

项目资源

  • 代码(本次受到文件大小限制,只传上去了assets文件夹)

MyUnity/弩箭打靶/Assets at master · BatallaCL/MyUnity (github.com)icon-default.png?t=N7T8https://github.com/BatallaCL/MyUnity/tree/master/%E5%BC%A9%E7%AE%AD%E6%89%93%E9%9D%B6/Assets

  • 视频

弩箭打靶_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1C64y177BA/?vd_source=a74c90c9380ff3fcdf00387a285570a6

游戏展示

代码实现

1:SkyBoxControl

        天空盒切换的控制器,使得游戏场景随着时间变化不断切换。

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

public class SBControl : MonoBehaviour
{
    public Material[] mats;//天空盒数组
    private int index = 0;
    public int changeTime = 3 ;//更换天空盒子的秒数
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log(System.DateTime.Now.Hour);
        InvokeRepeating("ChangeBox", 0, changeTime);
    }

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

        RenderSettings.skybox = mats[index];
        index++;
        index %= mats.Length;
    }
}

2:DayTimeControl

        时间切换(光照强度)的控制器,使得光照强度随着时间变化不断切换从而达到白天黑夜切换的效果。

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

public class DayTime : MonoBehaviour
{
    public float OrbitMinute = 0.1f;      //太阳转一周,所需的时间设为24分钟
    private float AnglePerSec = 0;

    //Real Time
    [Range(0, 1440)]
    public float sec = 0;
    private float min = 0;

    public DefaultTime defaultTime = DefaultTime.SystemTime;
    public enum DefaultTime
    {
        SystemTime,
        Random,
        Preset,
    }

    public float PresetTime = 7;

    [Header("Only Read")]
    //Game Time
    public float GameHour = 0;
    public float GameMin = 0;

    // Start is called before the first frame update
    void Start()
    {
        AnglePerSec = 360 / OrbitMinute / 60;
        if (defaultTime == DefaultTime.SystemTime)
            SetTime(System.DateTime.Now.Hour);
        else if (defaultTime == DefaultTime.Random)
            SetTime(Random.Range(0, 24));
        else if (defaultTime == DefaultTime.Preset)
            SetTime(PresetTime);
    }

    // Update is called once per frame
    void Update()
    {
        sec += Time.deltaTime;
        min = sec / 60;
        GameHour = (int)min % OrbitMinute;
        GameMin = (int)sec % 60;

        gameObject.transform.rotation = Quaternion.Euler(SunAngle(GameHour, GameMin), -90, 0);
    }

    float SunAngle(float hour)
    {
        return 360 / OrbitMinute * hour - 90;
    }

    float SunAngle(float hour, float minute)
    {
        return 360 / OrbitMinute * hour - 90 + AnglePerSec * minute;
    }

    void SetTime(float hour)
    {
        sec = hour * 60;
    }

}

3:CBMove

        实现了玩家控制的角色的第一人称视角的移动(WASD)和视角旋转(随鼠标旋转),使得玩家可以体验在场景中的移动和视角旋转功能。

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

public class CBMove : MonoBehaviour
{
    CharacterController playerController;

    Vector3 direction;

    public float speed = 1;
    public float jumpPower = 5;
    public float gravity = 7f;

    public float mousespeed = 5f;


    public float minmouseY = -45f;
    public float maxmouseY = 45f;

    float RotationY = 0f;
    float RotationX = 0f;

    public Transform agretctCamera;

    private Vector3 moveDirection = Vector3.zero;
    // Start is called before the first frame update
    void Start()
    {
        playerController = this.GetComponent<CharacterController>();
    }

    // Update is called once per frame
    void Update()
    {
        float _horizontal = Input.GetAxis("Horizontal");
        float _vertical = Input.GetAxis("Vertical");

        if (playerController.isGrounded)
        {
            direction = new Vector3(_horizontal, 0, _vertical);
            if (Input.GetKeyDown(KeyCode.Space))
                direction.y = jumpPower;
        }
        direction.y -= gravity * Time.deltaTime;
        playerController.Move(playerController.transform.TransformDirection(direction * Time.deltaTime * speed));

        RotationX += agretctCamera.transform.localEulerAngles.y + Input.GetAxis("Mouse X") * mousespeed;

        RotationY -= Input.GetAxis("Mouse Y") * mousespeed;
        RotationY = Mathf.Clamp(RotationY, minmouseY, maxmouseY);

        this.transform.eulerAngles = new Vector3(0, RotationX, 0);

        agretctCamera.transform.eulerAngles = new Vector3(RotationY, RotationX, 0);
    }
}

4:FireControl

        实现了弩箭的射击动作事件(左键)和弩箭角度调整(右键),是游戏玩法的核心代码。

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

public class FireControl : MonoBehaviour
{
    public bool fire_permisssion = true;
    public int arrow_num = 10;

    public ArrowControl arrow; //箭模板
    public Animator animator; //动画组件
    public Transform arrowPoint; //发射箭点

    public Transform bow_tem; //弓箭模板
    public Transform bow; //弓箭竖直
    public Transform bow_ro; //弓箭水平
    public float bow_rato = .1f; //竖直旋转速度
    public float ro_rato = .1f; //水平旋转速度

    public float hold_power; //蓄力强度
    public AnimationCurve hold_curve; //蓄力强度变化曲线
    public Vector3 mouse_position; //鼠标位置
    // Start is called before the first frame update
    void Start()
    {
        animator = bow_tem.GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {   if(fire_permisssion == true && arrow_num>0)
        //if(true)
        {
            //弓箭发射事件 左键控制 点下长按蓄力 松开后发射
            if (Input.GetKeyDown(KeyCode.Mouse0))
            {
                hold_power = 0;
                animator.SetBool("pull", true);
            }
            if (Input.GetKey(KeyCode.Mouse0))
            {
                animator.SetBool("hold", true);
                if (Time.deltaTime <= 3)
                {
                    hold_power += Time.deltaTime;
                    animator.SetFloat("hold_power", 0.7f - hold_power);
                }
            }
            if (Input.GetKeyUp(KeyCode.Mouse0))
            {
                animator.SetBool("pull", false);
                animator.SetBool("hold", false);
                animator.SetBool("shoot", true);
                ArrowControl temp = Instantiate(arrow, arrowPoint.position, arrowPoint.rotation);
                temp.force = hold_curve.Evaluate(hold_power);
                animator.SetBool("shoot", false);
                animator.SetFloat("hold_power", 0.5f);
                arrow_num--;
            }

        }
        
        //弓箭视角变化
        if(Input.GetKeyDown (KeyCode.Mouse1))
        {
            mouse_position = Input.mousePosition;
        }
        if (Input.GetKey(KeyCode.Mouse1))
        {
            bow_ro.Rotate( - Vector3.up * (mouse_position - Input.mousePosition).x * ro_rato);
            bow.Rotate(Vector3.right*(mouse_position - Input.mousePosition).y * bow_rato);
        }
        mouse_position = Input.mousePosition;
    }
}

5:RingController

        在箭矢射击到靶子后,通过碰撞检测事件实现箭矢碰撞后的停止、玩家分数的增加、箭矢的减少。

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

public class RingController : MonoBehaviour
{
    //当前环的分值
    public  int RingScore = 0;
    public ScoreRecorder sc_recorder;
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        
    }
    //碰撞检测,如果箭击中该环,就响应。
    void OnTriggerEnter(Collider temp)
    {
        //得到箭身
        Transform arrow = temp.gameObject.transform;
        if (temp == null)
        {
            return;
        }
        //有箭中靶
        if (temp.tag == "arrow")
        {
            //将箭的速度设为0
            arrow.GetComponent<Rigidbody>().velocity = new Vector3(0, 0, 0);
            //使用运动学运动控制
            arrow.GetComponent<Rigidbody>().isKinematic = true;
        }
    }
    void OnTriggerExit(Collider temp){
        //得到箭身
        Transform  arrow = temp.gameObject.transform;
        if(temp == null)
        {
            return ;
        }
        //有箭中靶
        if(temp.tag == "arrow"){
            //将箭的速度设为0
            arrow.GetComponent<Rigidbody>().velocity = new Vector3(0,0,0);
            //使用运动学运动控制
            arrow.GetComponent<Rigidbody>().isKinematic = true;
            //计分
            sc_recorder.RecordScore(RingScore);
            //标记箭为中靶
            //arrow.tag = "onTarget";
        }
    }
}

6:ScoreRecorder

        显示游戏的状态,包括分数、射击状态。箭矢剩余数目。

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

public class ScoreRecorder : MonoBehaviour
{
    public FireControl A;
    public int score;
    public Text scoreText;  //显示分数UI
    public Text fireText;  //显示射击状态UI
    public Text arrowNumText;  //显示弓箭数量UI
    // Start is called before the first frame update
    void Start()
    {
        score = 0;
    }

    private void Update()
    {
        if((A.transform.position.x>=20 && A.transform.position.x <= 70) && (A.transform.position.z>=10 && A.transform.position.z<=25))
        //if(true)
        {
            A.fire_permisssion = true;
            fireText.text = "射击:允许";
            arrowNumText.text = "次数:"+A.arrow_num;
        }
        else
        {
            A.fire_permisssion = false;
            fireText.text = "射击:禁止";
            arrowNumText.text = "次数:" + A.arrow_num;
        }
    }
    // Update is called once per frame
    public void RecordScore(int ringscore)
    {
        //增加新的值
        score += ringscore;
        scoreText.text = "分数:" + score;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值