附录
https://github.com/jASONSDFDSA/Game_4_Crossbow_Shooting_Terrain
Crossbow Shooting Terrain——Unity课程作业演示视频_哔哩哔哩_bilibili
项目介绍
使用Classical Crossbow以及动画来制作一个射击游戏。玩家可以操控弩弓在地图上移动,但只能在白色区域内射击,并有一定次数限制。玩家可以按下0来生成固定靶,按下1来生成移动靶。击中固定靶获得10分,击中移动靶获得20分。
如果玩家操控弩弓撞上树或者靶子则游戏结束。
代码简介
没有对代码进行规范化的整理,但对资源做了整理。其中Main Camera使用了天空盒组件,并且被绑在了弩弓上面。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class Crowssbow : MonoBehaviour
{
public Transform target; // 这是你要跟随的目标模型的Transform组件
public Vector3 offset;
void Update()
{
if (target != null)
{
// 设置相机的位置与目标模型的位置一致
transform.position = target.position + offset;
}
}
}
然后,编写脚本让弩弓能够在地图上游走、鼠标操控视角,并显示射击次数。
using System;
using UnityEngine;
using UnityEngine.UIElements;
public class PlayerController : MonoBehaviour
{
public float moveSpeed = 5f; // 移动速度
public float sensitivity = 1f; // 鼠标灵敏度
public float bulletSpeed = 10f;
public GameObject bulletPrefab; // 子弹预制体
public Vector3 fireOffset;
public Transform firePoint;
public int movingTargetChance = 100;
public int fixedTargetChance = 6;
private int movingShoot = 0;
private int fixedShoot = 0;
private Animator animator;
private bool isHolding;
private int inRange;
private float coolDown;
private float castAnimation;
private bool canControl;
private GUIStyle fontStyle;
private GUIStyle fontStyle2;
void Start()
{
fontStyle = new GUIStyle();
fontStyle.normal.background = null; //设置背景填充
fontStyle.normal.textColor= new Color(1,0,0); //设置字体颜色
fontStyle.fontSize = 40;
fontStyle2 = new GUIStyle();
fontStyle2.normal.background = null; //设置背景填充
fontStyle2.normal.textColor= new Color(0,0,0); //设置字体颜色
fontStyle2.fontSize = 30;
// 获取Animator组件
animator = GetComponent<Animator>();
isHolding = false;
canControl = true;
inRange = 0;
coolDown = 0;
}
void Update()
{
if (canControl)
{
inRange = CheckRange();
if (inRange != 0)
{
// 长按鼠标时处于 "holding" 状态
if ((inRange == 1 && fixedShoot < fixedTargetChance) ||
(inRange == 2 && movingShoot < movingTargetChance))
{
if (Input.GetMouseButton(0) && coolDown - Time.time <= -1) // 0 表示左键
{
isHolding = true;
castAnimation = Time.time;
}
else
{
// 松开鼠标时切换到 "fire" 状态
if (isHolding && Time.time - castAnimation >= 0.5)
{
isHolding = false;
coolDown = Time.time;
SetFireState();
ShootBullet();
if (inRange == 1)
{
fixedShoot++;
}
else if (inRange ==2)
{
movingShoot++;
}
}
}
}
// 更新Animator Controller 中的参数
UpdateAnimatorParameters();
}
// 获取键盘输入
float horizontalInput = Input.GetAxisRaw("Horizontal");
float verticalInput = Input.GetAxisRaw("Vertical");
// 计算移动方向
Vector3 movement = new Vector3(-verticalInput, 0f, horizontalInput).normalized;
// 移动模型
Move(movement);
// 获取鼠标移动输入
float mouseX = Input.GetAxisRaw("Mouse X");
float mouseY = Input.GetAxisRaw("Mouse Y");
// 根据鼠标输入调整摄像机的旋转
RotateCamera(mouseX, mouseY);
}
}
void Move(Vector3 movement)
{
// 根据输入移动模型
Vector3 targetPosition = transform.position + movement * moveSpeed * Time.deltaTime;
// 移动模型到目标位置
transform.position = targetPosition;
}
void OnCollisionEnter(Collision other)
{
GameOver();
}
void RotateCamera(float mouseX, float mouseY)
{
// 获取当前摄像机的旋转
Vector3 currentRotation = transform.eulerAngles;
// 水平旋转(左右移动)
currentRotation.y += mouseX * sensitivity;
// 垂直旋转(上下移动),限制在-90到90度之间
currentRotation.x = Mathf.Clamp(currentRotation.x - mouseY * sensitivity, -90f, 90f);
// 应用旋转
transform.eulerAngles = currentRotation;
}
void SetFireState()
{
// 触发 "fire" 状态
animator.SetTrigger("Fire");
}
void ShootBullet()
{
// 检查是否设置了子弹预制体和发射位置
if (bulletPrefab != null)
{
Quaternion existingRotation = transform.rotation;
// 获取 existingRotation 的欧拉角
Vector3 existingEulerAngles = existingRotation.eulerAngles;
// 增加 x 值
existingEulerAngles.x -= 90f;
// 创建一个新的 Quaternion
Quaternion newRotation = Quaternion.Euler(existingEulerAngles);
// 生成子弹
GameObject bullet = Instantiate(bulletPrefab, firePoint.position + fireOffset, newRotation);
// 获取子弹上的刚体组件
Rigidbody bulletRigidbody = bullet.GetComponent<Rigidbody>();
// 检查刚体组件是否存在
if (bulletRigidbody != null)
{
// 为子弹添加速度
bulletRigidbody.velocity = bullet.transform.forward * bulletSpeed;
}
}
}
void UpdateAnimatorParameters()
{
// 更新Animator Controller 中的参数
animator.SetBool("Holding", isHolding);
}
void GameOver()
{
canControl = false;
}
int CheckRange()
{
Vector3 p = transform.position;
float px = p.x;
float pz = p.z;
int plane = 0;
if (px <= 2.5f && px >= -7.5f)
{
if (pz >= -5 && pz <= 5)
{
plane = 1;
}
else if (pz >= 10 && pz <= 20)
{
plane = 2;
}
}
return plane;
}
void OnGUI()
{
GUI.Label(new Rect(0, Screen.height - 40, 200, 40), "Shoot " + fixedShoot + "/" + fixedTargetChance +" times in plane 1", fontStyle2);
GUI.Label(new Rect(Screen.width - 400, Screen.height - 40, 200, 40), "Shoot " + movingShoot + "/" + movingTargetChance +" times in plane 2", fontStyle2);
if (!canControl)
{
GUI.Label(new Rect(Screen.width/2 - 60, Screen.height/2 - 40, 100, 80), "Game Over!", fontStyle);
}
}
}
给弓箭加点简单的物理逻辑,让其被射出后能够正常运动。
using UnityEngine;
public class ProjectileController : MonoBehaviour
{
private Rigidbody rb;
public int scoreValue = 10;
public int movingScoreValue = 20;
void Start()
{
// 获取刚体组件
rb = GetComponent<Rigidbody>();
}
void OnCollisionEnter(Collision collision)
{
// 在碰撞发生时停止刚体运动
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
// 可以添加其他的停止逻辑,比如播放停止动画或者触发其他事件
if (collision.gameObject.CompareTag("Target"))
{
// 处理计分逻辑
ScoreManager.Instance.AddScore(scoreValue);
// 销毁弩箭对象
Destroy(gameObject);
}
if (collision.gameObject.CompareTag("Moving Target"))
{
// 处理计分逻辑
ScoreManager.Instance.AddScore(movingScoreValue);
// 销毁弩箭对象
Destroy(gameObject);
}
}
}
然后,用ScoreManager来计分与显示分数。
using UnityEngine;
using UnityEngine.UI;
public class ScoreManager : MonoBehaviour
{
public static ScoreManager Instance; // 单例实例
private GUIStyle fontStyle;
private int score = 0; // 分数
void Awake()
{
// 创建单例
if (Instance == null)
{
Instance = this;
}
else
{
Destroy(gameObject);
}
}
void Start()
{
score = 0;
fontStyle = new GUIStyle();
fontStyle.normal.background = null; //设置背景填充
fontStyle.normal.textColor= new Color(1,0,0); //设置字体颜色
fontStyle.fontSize = 40; //字体大小
}
public void AddScore(int value)
{
score += value;
}
void OnGUI()
{
GUI.Label(new Rect(0, 0, 100, 30), "Score: " + score, fontStyle);
}
}
用TargetCreator来生成靶子
using UnityEngine;
public class TargetCreator: MonoBehaviour
{
public GameObject myPrefab1; // 引用你的预制体
public GameObject myPrefab2;
public float spawnRadius = 5f;
void Update()
{
// 在Update函数中检测用户输入,例如按下空格键
if (Input.GetKeyDown(KeyCode.Keypad0))
{
Vector3 randomPosition = transform.position + Random.onUnitSphere * spawnRadius;
randomPosition.y = 2f;
// 使用Instantiate生成预制体的实例
Instantiate(myPrefab1, randomPosition, myPrefab1.transform.rotation);
// 使用Instantiate生成预制体的实例
}
if (Input.GetKeyDown(KeyCode.Keypad1))
{
Vector3 randomPosition = transform.position;
randomPosition.y = 2f;
// 使用Instantiate生成预制体的实例
Instantiate(myPrefab2, randomPosition, myPrefab2.transform.rotation);
}
}
}