赞
踩
地形:使用地形组件,上面有草、树;
天空盒:使用天空盒,天空可随时间变化
固定靶:有一个以上固定的靶标;
运动靶:有一个以上运动靶标,运动轨迹,速度使用动画控制;
射击位:地图上应标记若干射击位,仅在射击位附近可以拉弓射击;
驽弓动画:支持蓄力半拉弓,然后 hold,择机 shoot;
游走:玩家的驽弓可在地图上游走,不能碰上树和靶标等障碍;
碰撞与计分:在射击位,射中固定靶+10分,移动靶+20分。
导入了assets store的三个资源包,分别是天空盒、靶子、弓箭资源包,ArrowController用于射箭碰撞检测控制,BowController用于控制拉弓蓄力及射击,BowMovement用于控制弓箭(摄像头)移动及视角转动,SwitchSky用于控制天空盒随时间变化。
新建一个空物体挂载SwitchSky,配置mats数组为需要切换的天空盒材质,ChangeTime为天空盒切换时间,这里的三个天空盒Material均为资源包中下载的。
ChangeBox()
是一个公共方法。它在指定的时间间隔内被 InvokeRepeating()
方法重复调用。在这个方法中,它通过改变 currentIndex
和 nextIndex
来更新当前和下一个天空盒的索引,并将 RenderSettings.skybox
设置为 mats
数组中的下一个天空盒材质。
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class SwitchSky : MonoBehaviour
- {
- public Material[] mats;
- private int currentIndex = 0;
- private int nextIndex = 1;
- public int changeTime; // 更换天空盒子的秒数
-
- void Start()
- {
- RenderSettings.skybox = mats[currentIndex];
- InvokeRepeating("ChangeBox", 0, changeTime);
- }
-
- public void ChangeBox()
- {
- currentIndex = nextIndex;
- nextIndex = (nextIndex + 1) % mats.Length;
- RenderSettings.skybox = mats[currentIndex]; // 更换天空盒材质
- }
- }
-
导入资源包后将任意Demo拖入对象列表即可获得对应天空盒和地形,根据游戏需要设置地形范围大小,环境中还有草、栅栏、树、石头等,这些物体的预制体中需要添加Collider,选择合适的Collider以保证玩家的驽弓可在地图上游走,不能碰上树和靶标等障碍。
直接拖入资源包中的靶子预制体即可使用,靶子数量根据个人游戏设计添加。
固定靶添加标签staticTarget,移动靶添加标签MovingTarget,地形添加标签Ground.
移动靶分为左右移动和上下移动两种,以上下移动的动画UpDownTarget为例,新建Animation——UpDownTarget,拉到需要做成移动靶的靶子对象上自动建立Animator组件,Animation通过编辑Curves或录制设计上下移动的动画。
新建两个任意形状对象作为射击区域,这里选择建立两个圆形区域,白色圆形区域附近有两个移动靶,而绿色移动区域均为固定靶,两个射击区对象均添加标签Ground.后续代码实现仅在射击位附近可以拉弓射击。
弓箭CrossBow的动画控制器CrossbowAni设置如下,Empty到EmptyPull的转移条件为isPulling = Ture,EmptyPull到Shoot的转移条件为Fire = True.
射击控制由BowController实现,可以检测
1.Isinarea()
方法用于检查玩家是否处于特定区域内。
检查当前玩家是否位于预设的两个区域中的任意一个内,通过计算玩家位置和两个区域点之间的距离来判断。
如果玩家在指定区域内:
isPulling
为 true
。pullStrength
。Fire()
方法发射箭矢,并根据 pullStrength
来设置箭矢的发射力度。2.Fire()
方法:
firePoint
位置实例化箭矢预制体,并设置箭矢的初始位置和方向。Rigidbody
组件,并根据 holdForce
(拉弓力度)施加力,使箭矢飞行- using System.Collections;
- using System.Collections.Generic;
- using TMPro;
- using UnityEngine;
-
- public class BowController : MonoBehaviour
- {
-
- Animator animator;
- bool isPulling = false;
- float pullDuration = 0f;
- public float maxPullDuration = 2f; // 最长拉弓时间
- public float arrowSpeed = 1f; // 箭矢速度
- public GameObject arrowObj;
- float pullStrength;
- public Transform firePoint;
- public Camera maincam;
- void Start()
- {
- // 获取弓上的Animator组件
- animator = GetComponent<Animator>();
- }
-
- void Update()
- {
- if (Isinarea())
- {
- Debug.Log("在区域内");
- // 当按下空格键时触发状态切换
- if (Input.GetKeyDown(KeyCode.Space))
- {
- pullDuration = 0;
- animator.SetBool("Fire", false);
- animator.SetFloat("Power", 0);
- isPulling = true;
- animator.SetBool("isPulling", true);
- //animator.SetBool("Holding", false);
- }
-
- // 持续按下空格键时记录按下时间,决定拉弓的强度
- if (isPulling)
- {
- pullDuration += Time.deltaTime;
- // 将按下的时间映射到0到1的范围,作为拉弓强度的参数
- pullStrength = Mathf.Clamp01(pullDuration / maxPullDuration);
- animator.SetFloat("Power", pullStrength);
- }
-
-
- if (Input.GetKeyUp(KeyCode.Space))
- {
-
- isPulling = false;
- //animator.SetBool("Holding", true);
- animator.SetBool("isPulling", false);
-
- }
- // 当点击鼠标左键时触发射击
- if (Input.GetMouseButtonDown(0))
- {
- animator.SetBool("Fire", true);
- animator.SetFloat("Power", 0);
- // animator.SetBool("Holding", false);
- Fire(pullStrength);
- }
- }
- }
- public void Fire(float holdForce)
- {
- GameObject arrow = Instantiate<GameObject>(Resources.Load<GameObject>("prefabs/Arrow"));
- arrow.AddComponent<ArrowController>();
- ArrowController arrowController = arrow.GetComponent<ArrowController>();
- arrowController.cam = maincam;
- arrow.transform.position = firePoint.transform.position;
- arrow.transform.rotation = Quaternion.LookRotation(this.transform.forward);
- Rigidbody rd = arrow.GetComponent<Rigidbody>();
- if (rd != null)
- {
- rd.AddForce(this.transform.forward * 60 * holdForce);
- Debug.Log(arrow.transform.rotation);
- }
- }
- bool Isinarea()
- {
- Vector3 currentPosition = transform.position;
- Vector3 area1 = new Vector3(-99.12f, 14.86f, 153.65f);
- Vector3 area2 = new Vector3(-93.8506f, 15.3659f, 83.4803f);
- float distance1 = Vector3.Distance(currentPosition, area1); // 计算目标点和玩家位置之间的距离
- float distance2 = Vector3.Distance(currentPosition, area2);
- float radius = 10f; // 圆的半径
-
- if (distance1 <= radius || distance2 <= radius)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
-
- }
1.变量定义:
public Transform tourCamera;
:用于存储游览相机的 Transform 组件。moveSpeed
, rotateSpeed
, shiftRate
:控制相机移动和旋转的速度参数。minDistance
:控制相机与不可穿透表面的最小距离。2.Update() 方法:
GetDirection()
:检测玩家输入,并获取相机移动的方向。Physics.Raycast()
方法检查是否离不可穿透表面过近,如果是,消除垂直于不可穿透表面的运动速度分量,以防止相机穿透表面。Translate()
方法移动相机。3.GetDirection() 方法:
Input.GetMouseButton(1)
检测鼠标右键是否按下,如果按下,根据鼠标移动来控制相机的旋转。4.V3RotateAround() 方法:
Vector3
绕旋转中心旋转指定角度后所得到的向量。- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class BowMovement : MonoBehaviour
- {
- // 在场景中游览的相机(不要给相机加碰撞器!)
- public Transform tourCamera;
- #region 相机移动参数
- public float moveSpeed = 20.0f;
- public float rotateSpeed = 150.0f;
- public float shiftRate = 2.0f;// 按住Shift加速
- public float minDistance = 0.5f;// 相机离不可穿过的表面的最小距离(小于等于0时可穿透任何表面)
- #endregion
- #region 运动速度和其每个方向的速度分量
- private Vector3 direction = Vector3.zero;
- private Vector3 speedForward;
- private Vector3 speedBack;
- private Vector3 speedLeft;
- private Vector3 speedRight;
- private Vector3 speedUp;
- private Vector3 speedDown;
- #endregion
- void Start()
- {
- if (tourCamera == null) tourCamera = gameObject.transform;
-
- }
- void Update()
- {
- GetDirection();
- // 检测是否离不可穿透表面过近ss
- RaycastHit hit;
- while (Physics.Raycast(tourCamera.position, direction, out hit, minDistance))
- {
- // 消去垂直于不可穿透表面的运动速度分量
- float angel = Vector3.Angle(direction, hit.normal);
- float magnitude = Vector3.Magnitude(direction) * Mathf.Cos(Mathf.Deg2Rad * (180 - angel));
- direction += hit.normal * magnitude;
- }
- if (tourCamera.localPosition.y > 25.0f)
- {
- tourCamera.localPosition = new Vector3(tourCamera.localPosition.x, 25.0f, tourCamera.localPosition.z);
- }
- if (tourCamera.localPosition.y < 12.0f)
- {
- tourCamera.localPosition = new Vector3(tourCamera.localPosition.x, 12.0f, tourCamera.localPosition.z);
- }
- tourCamera.Translate(direction * moveSpeed * Time.deltaTime, Space.World);
- }
- private void GetDirection()
- {
- #region 加速移动
- if (Input.GetKeyDown(KeyCode.LeftShift)) moveSpeed *= shiftRate;
- if (Input.GetKeyUp(KeyCode.LeftShift)) moveSpeed /= shiftRate;
- #endregion
- #region 键盘移动
- // 复位
- speedForward = Vector3.zero;
- speedBack = Vector3.zero;
- speedLeft = Vector3.zero;
- speedRight = Vector3.zero;
- speedUp = Vector3.zero;
- speedDown = Vector3.zero;
- // 获取按键输入
- if (Input.GetKey(KeyCode.W)) speedForward = tourCamera.forward;
- if (Input.GetKey(KeyCode.S)) speedBack = -tourCamera.forward;
- if (Input.GetKey(KeyCode.A)) speedLeft = -tourCamera.right;
- if (Input.GetKey(KeyCode.D)) speedRight = tourCamera.right;
- if (Input.GetKey(KeyCode.E)) speedUp = Vector3.up;
- if (Input.GetKey(KeyCode.Q)) speedDown = Vector3.down;
- direction = speedForward + speedBack + speedLeft + speedRight + speedUp + speedDown;
- #endregion
- #region 鼠标旋转
- if (Input.GetMouseButton(1))
- {
- // 转相机朝向
- tourCamera.RotateAround(tourCamera.position, Vector3.up, Input.GetAxis("Mouse X") * rotateSpeed * Time.deltaTime);
- tourCamera.RotateAround(tourCamera.position, tourCamera.right, -Input.GetAxis("Mouse Y") * rotateSpeed * Time.deltaTime);
- // 转运动速度方向
- direction = V3RotateAround(direction, Vector3.up, Input.GetAxis("Mouse X") * rotateSpeed * Time.deltaTime);
- direction = V3RotateAround(direction, tourCamera.right, -Input.GetAxis("Mouse Y") * rotateSpeed * Time.deltaTime);
- }
- #endregion
- }
- /// <summary>
- /// 计算一个Vector3绕旋转中心旋转指定角度后所得到的向量。
- /// </summary>
- /// <param name="source">旋转前的源Vector3</param>
- /// <param name="axis">旋转轴</param>
- /// <param name="angle">旋转角度</param>
- /// <returns>旋转后得到的新Vector3</returns>
- public Vector3 V3RotateAround(Vector3 source, Vector3 axis, float angle)
- {
- Quaternion q = Quaternion.AngleAxis(angle, axis);// 旋转系数
- return q * source;// 返回目标点
- }
-
- }
碰撞逻辑在ArrowController实现,分数改变传递到ui.score变量显示。
1.变量定义:
private Rigidbody rb;
:箭矢的刚体组件,用于控制箭矢的物理行为。public float Score = 0;
:箭矢的得分,可能是在箭矢击中目标时增加的分数。public Camera cam;
:用于获取相机以访问 Main
脚本。private Main ui;
:对游戏界面的引用,用于更新得分。2.Start()
方法:
Main
脚本。3.OnCollisionEnter(Collision collision)
方法:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.SocialPlatforms.Impl;
-
- public class ArrowController : MonoBehaviour
- {
- // Start is called before the first frame update
- private Rigidbody rb;
- public float Score = 0;
-
- public Camera cam;
- private Main ui;
- void Start()
- {
- rb = GetComponent<Rigidbody>();
- ui = cam.GetComponent<Main>();
- }
-
- // Update is called once per frame
- void Update()
- {
-
- }
- private void OnCollisionEnter(Collision collision)
- {
- if (collision.gameObject.CompareTag("staticTarget"))
- {
- rb.velocity = Vector3.zero;
- rb.angularVelocity = Vector3.zero;
- ui.Score += 10;
- Destroy(gameObject);
-
- }
- else if (collision.gameObject.CompareTag("MovingTarget"))
- {
- rb.velocity = Vector3.zero;
- rb.angularVelocity = Vector3.zero;
- ui.Score += 20;
- Destroy(gameObject);
- }
- else if (collision.gameObject.CompareTag("Ground"))
- {
- rb.velocity = Vector3.zero;
- rb.angularVelocity = Vector3.zero;
- Destroy(gameObject);
- }
-
- }
- }
Main实现显示分数和游戏提示,挂载在主摄像机Cam_fps(1)上。
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Main : MonoBehaviour
- {
- // Start is called before the first frame update
- public float Score = 0;
- void Start()
- {
- }
-
- // Update is called once per frame
- void Update()
- {
-
- }
- private void OnGUI()
- {
- GUIStyle style = new GUIStyle();
- style.fontSize = 15;
- style.normal.textColor = Color.white;
-
- // 定义游戏介绍文本内容
- string introText = "天空10s自动切换\nwasd --移动弓弩\nSpace --蓄力拉弓\n鼠标左键 --射箭\n鼠标右键 --调整角度";
-
- // 在屏幕左上角绘制游戏介绍文本
- GUI.Label(new Rect(10, 10, 300, 100), introText, style);
- style.fontSize = 24;
- style.normal.textColor = Color.white;
- // 在屏幕右上角显示分数
- GUI.Label(new Rect(Screen.width - 150, 20, 150, 30), "Score: " + Score, style);
- }
- }
参考博客:unity射箭小游戏-CSDN博客
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。