开发第一人称射箭游戏
游戏要求:
- 地形:使用地形组件,上面有草、树;
- 天空盒:使用天空盒,天空可随玩家位置 或 时间变化 或 按特定按键切换天空盒;
- 固定靶:有一个以上固定的靶标;
- 运动靶:有一个以上运动靶标,运动轨迹,速度使用动画控制;
- 射击位:地图上应标记若干射击位,仅在射击位附近可以拉弓射击,每个位置有 n 次机会;
- 驽弓动画:支持蓄力半拉弓,然后 hold,择机 shoot;
- 游走:玩家的驽弓可在地图上游走,不能碰上树和靶标等障碍;
- 碰撞与计分:在射击位,射中靶标的相应分数,规则自定;
游戏效果图:
游戏编程文件如下:
一、实现玩家走动和转动视角
通过CameraMgr和CrossbowMove来实现视角的转动
CameraMgr
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraMgr : MonoBehaviour
{
//鼠标x轴灵敏度
public float mouseXSensitivity = 80f;
//人物
private Transform player;
//旋转角度
float xRotation = 0f;
public bool isMove;
private void Start()
{
player = transform.parent.transform;
isMove = true;
}
// Update is called once per frame
void Update()
{
if (isMove)
{
float mouseX = Input.GetAxis("Mouse X") * mouseXSensitivity * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * mouseXSensitivity * Time.deltaTime;
xRotation -= mouseY;
//y轴最大旋转角度为正负90;
xRotation = Mathf.Clamp(xRotation, -45f, 10f);
transform.localRotation = Quaternion.Euler(xRotation, 0f, 0f);
player.Rotate(Vector3.up * mouseX);
}
}
}
CrossbowMove
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CrossbowMove : MonoBehaviour
{
//人物控制器
private CharacterController controller;
//人物移动速度
public float speed = 2f;
public float gravity = -15f;
Vector3 velocity;
//是否可以移动摄像机
bool isMove;
private CameraMgr cameraMgr;
private void Start()
{
controller = GetComponent<CharacterController>();
cameraMgr = transform.GetChild(0).GetComponent<CameraMgr>();
isMove = true;
Cursor.lockState = CursorLockMode.Locked;
Cursor.visible = false;
}
// Update is called once per frame
void Update()
{
//按键Q隐藏 或者显示鼠标
if (Input.GetKeyDown(KeyCode.Q))
{
isMove = !isMove;
isMove = cameraMgr.isMove;
if (isMove == false)
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
}
if (isMove)
{
Move();
}
}
//移动
public void Move()
{
//键盘输入
float x = Input.GetAxis("Horizontal");
float z = Input.GetAxis("Vertical");
Vector3 move = transform.right * x + transform.forward * z;
controller.Move(move * speed * Time.deltaTime);
velocity.y += gravity * Time.deltaTime;
controller.Move(velocity * Time.deltaTime);
}
}
二、弩的射击与蓄力实现
弩的射击通过CrossbowMgr来进行管理,蓄力的逻辑根据按鼠标左键时长来确定。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class CrossbowMgr : MonoBehaviour
{
Animator anim;
//蓄力时间
float downTime;
//飞行的力
float arrowForce;
//是否蓄力
bool isEnergyStorage;
//箭预制体
public GameObject arrowPrefab;
//发射点
public Transform firePoint;
public RawImage arrowCameraRawImg;
public bool isFire;
//是否在发射点
public bool isFirePos;
public FiringPosition firingPosition;
public GameObject firingPosText;
public GameObject gameOver;
void Start()
{
anim = GetComponent<Animator>();
isEnergyStorage = true;
}
void Update()
{
if (isFire)
{
//蓄力功能
if (isEnergyStorage)
{ //蓄力阶段按下鼠标左键
if (Input.GetMouseButtonDown(0))
{
//进入蓄力动画
arrowCameraRawImg.enabled = false;
anim.SetTrigger("hold");
}//鼠标按下阶段
else if (Input.GetMouseButton(0))
{
//计算蓄力时间
downTime += Time.deltaTime;
//设置蓄力动画
anim.SetFloat("hold_power", downTime);
}//鼠标抬起阶段
else if (Input.GetMouseButtonUp(0))
{
//蓄力取消
isEnergyStorage = false;
//为飞行的力赋值
arrowForce = downTime;
//清空蓄力时间
downTime = 0;
}
}
else//发射功能
{ //蓄力完成发射
if (Input.GetMouseButtonUp(0))
{
//播放动画
anim.SetTrigger("shoot");
//设置可以重新蓄力
isEnergyStorage = true;
//清空所有箭
ResetArrow();
//发射弩箭
FireArrow();
}
}
}
}
//弩箭发射
public void FireArrow()
{
firingPosition.count--;
firingPosText.transform.GetComponent<Text>().text = "当前位置还可以发射次数为:" + firingPosition.count.ToString();
//实例化箭
GameObject arrow = Instantiate(arrowPrefab, firePoint.position, firePoint.rotation);
//获得刚体
Rigidbody arrowRigidbody = arrow.GetComponent<Rigidbody>();
//根据蓄力大小发射弩箭
arrowRigidbody.velocity = transform.forward * arrowForce * 30f;
Invoke("AllFirePosCount", 3f);
}
public void ResetArrow()
{
var arrowObjs = GameObject.FindGameObjectsWithTag("Arrow");
for (int i = 0; i < arrowObjs.Length; i++)
{
Destroy(arrowObjs[i]);
}
}
/// <summary>
/// 检测全部靶子是否可以发射
/// </summary>
public void AllFirePosCount()
{
var firePosObjs = GameObject.FindGameObjectsWithTag("FirePos");
int tempInt = 0;
for (int i = 0; i < firePosObjs.Length; i++)
{
if (firePosObjs[i].transform.GetComponent<FiringPosition>().count <= 0)
{
tempInt++;
}
}
if (tempInt >= 5)
{
gameOver.SetActive(true);
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
}
}
实现弩蓄力的动画如下:
其中new hold为混合树
三、地形
使用的资源包为tree9,绘制好地形和树,加入墙体,并手动为预制件添加组件“Capsule Collider”,以增加碰撞体。
四、靶的制作
游戏共有三个固定靶,两个移动靶,为运动靶添加动画。通过窗口->动画->动画打开动画编辑器。选中运动靶游戏对象->选择添加动画->添加运动靶的位置属性->设置三个关键帧,初始帧坐标为(0,0,0),末尾帧的位置坐标和初始帧相同。然后创建动画控制器,设置如下动画转移:
五、天空盒
使用Fantasy Sky FREE的天空素材。当按下f1时切换。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//设置天空盒
public class SetSkyBox : MonoBehaviour
{
public Material[] skyMaterials;
private Skybox skybox;
int index;
void Start()
{
//获取组件
skybox = GetComponent<Skybox>();
//设置材质
skybox.material = skyMaterials[index];
}
void Update()
{
if (Input.GetKeyDown(KeyCode.F1))
{
SetMaterial();
}
}
//设置天空盒
public void SetMaterial()
{
index++;
if (index >= skyMaterials.Length)
{
index = 0;
}
skybox.material = skyMaterials[index];
}
}
六、记分
根据射到靶的不同位置进行计分,右上角会显示总的分数,当每个位置的箭都用完后,提示重新开始。
视频地址:unity射箭游戏-其他-高清完整正版视频在线观看-优酷 (youku.com)
代码地址:3d-game/w11/Reset at main · wujf37/3d-game (github.com)