- 基础分(2分):有博客;
- 1-3分钟视频(2分):视频呈现游戏主要游玩过程;
- 地形(2分):使用地形组件,上面有草、树;
- 天空盒(2分):使用天空盒,天空可随玩家位置 或 时间变化 或 按特定按键切换天空盒;
- 固定靶(2分):有一个以上固定的靶标;
- 运动靶(2分):有一个以上运动靶标,运动轨迹,速度使用动画控制;
- 射击位(2分):地图上应标记若干射击位,仅在射击位附近可以拉弓射击,每个位置有 n 次机会;
- 驽弓动画(2分):支持蓄力半拉弓,然后 hold,择机 shoot;
- 游走(2分):玩家的驽弓可在地图上游走,不能碰上树和靶标等障碍;
- 碰撞与计分(2分):在射击位,射中靶标的相应分数,规则自定;
用了Classicbow的模型与动画
1、地形不需要设定代码,只需要保证刚体和碰撞就可以完成作业要求
2、天空盒子,我这里下载了免费的allsky资源,3秒轮播
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class changeskybox : MonoBehaviour
{
// 天空盒材质的数组
public Material[] skyboxes;
// 用于轮播天空盒的计时器
private float timer = 0f;
// 每隔多少秒轮播一次天空盒
public float changeInterval = 3f;
// 当前显示的天空盒的索引
private int currentSkyboxIndex = 0;
// 在游戏开始时初始化天空盒
void Start()
{
if (skyboxes.Length > 0)
{
RenderSettings.skybox = skyboxes[0];
}
}
// 每帧更新一次
void Update()
{
// 更新计时器
timer += Time.deltaTime;
// 如果计时器超过了更改间隔,那么更改天空盒
if (timer > changeInterval)
{
// 重置计时器
timer = 0f;
// 更新天空盒索引
currentSkyboxIndex = (currentSkyboxIndex + 1) % skyboxes.Length;
// 更改天空盒
RenderSettings.skybox = skyboxes[currentSkyboxIndex];
}
}
}
3、固定靶,运动靶,用多个圆柱叠在一起了,但后面图实现简单,就先直接使用命中就得分的机制,没有按环给分,之后todo。不需要代码,但还是需要colider,不需要添加刚体,否则会被撞飞。
运动靶就通过简单的动画实现移动:
4、射击位,设定射击次数
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpotController : MonoBehaviour
{
public int shots;
// Start is called before the first frame update
void Start()
{
shots = 3;
}
// Update is called once per frame
void Update()
{
}
}
5、弩弓动画
用了blend tree,通过检测鼠标的点击控制发射。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CrossbowController : MonoBehaviour
{
public float mouseSensitivity = 100.0f;
float force;
const float maxForce = 0.5f;
const float chargeRate = 0.1f;
Animator animator;
float mouseDownTime;
bool isCharging;
public int speed;
public bool ready_to_shoot;
private bool atSpot = false;
public SpotController[] spots;
private SpotController inspot;
string message;
void Start()
{
Debug.Log("Self position: " + this.gameObject.transform.position);
Cursor.lockState = CursorLockMode.Locked;
animator = GetComponent<Animator>();
ready_to_shoot = false;
inspot = null;
message = "";
}
void Update()
{
Debug.Log("Self position: " + this.gameObject.transform.position);
atSpot = false; // Set atSpot to false at the start of each update
ready_to_shoot = false; // Set ready_to_shoot to false at the start of each update
foreach (SpotController s in spots)
{
Vector2 position1 = new Vector2(s.gameObject.transform.position.x, s.gameObject.transform.position.z);
Vector2 position2 = new Vector2(this.gameObject.transform.position.x, this.gameObject.transform.position.z);
float distance = Vector2.Distance(position1, position2);
if (distance <= 3)
{
atSpot = true;
inspot = s;
ready_to_shoot = inspot.shots > 0 ? true : false;
break; // If a spot is found where distance <= 5, we break the loop
}
}
if (ready_to_shoot)
{
if (Input.GetMouseButtonDown(0))
{
Debug.Log("Mouse button down, start charging");
mouseDownTime = Time.time;
isCharging = true;
animator.SetTrigger("pull");
}
if (isCharging)
{
Debug.Log("Charging");
float holdTime = Time.time - mouseDownTime;
force = Mathf.Min(holdTime / 0.3f * chargeRate, maxForce);
animator.SetFloat("power", force + 0.5f);
}
if (Input.GetMouseButtonUp(0) && isCharging)
{
Debug.Log("Mouse button up, stop charging and fire");
isCharging = false;
float holdTime = Time.time - mouseDownTime;
force = Mathf.Min(holdTime / 0.3f * chargeRate, maxForce);
animator.SetFloat("hold_power", force + 0.5f);
StartCoroutine(DelayedFireCoroutine(force));
animator.SetTrigger("shoot");
}
}
else
{
Debug.Log("Not ready to shoot");
}
}
IEnumerator DelayedFireCoroutine(float f)
{
Debug.Log("Ready to fire!!");
yield return new WaitForSeconds(0f);
fire(f);
inspot.shots--;
}
public void fire(float f)
{
Debug.Log("Firing with force: " + f);
GameObject arrow = Instantiate<GameObject>(Resources.Load<GameObject>("Prefabs/Arrow"));
ArrowController aw = arrow.AddComponent<ArrowController>();
Transform childTransform1 = transform.Find("弦");
aw.transform.position = childTransform1.position;
aw.transform.rotation = Quaternion.LookRotation(this.transform.up);
Rigidbody arrow_db = arrow.GetComponent<Rigidbody>();
arrow.tag = "Arrow";
arrow_db.AddForce(100 * f * this.transform.up);
}
void OnCollisionEnter(Collision collider)
{
this.gameObject.GetComponent<Rigidbody>().isKinematic = true;
}
void OnCollisionExit(Collision collision)
{
this.gameObject.GetComponent<Rigidbody>().isKinematic = false;
}
void OnGUI()
{
GUIStyle style = new GUIStyle(GUI.skin.label);
style.fontSize = 20; // Set the font size to 20
if (atSpot)
{
message = "您已到达射击位,剩余射击次数:" + inspot.shots;
}
else
{
message = "";
}
GUI.Label(new Rect(300, 350, 500, 100), message, style);
}
}
6、箭
包括了是否射中的检测,发射的速度,以及得分计算,通过一个全局的静态S
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ArrowController : MonoBehaviour
{
//Transform target;
//Vector3 delta;
Rigidbody rb;
private Transform target; // 靶的 Transform 组件
private Vector3 offset; // 箭与靶的位置偏移
bool isMove;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody>();
isMove = false;
offset = new Vector3(0,0,0);
}
void OnCollisionEnter(Collision collider)
{
if (collider.gameObject.tag == "Target")
{
this.gameObject.GetComponent<Rigidbody>().isKinematic = true;
isMove = false;
//Debug.Log(collider.gameObject.name);
ScoreController.S += 1;
Debug.Log("Score: " + ScoreController.S); // 打印得分
}
if (collider.gameObject.tag == "MoveTarget")
{
this.gameObject.GetComponent<Rigidbody>().isKinematic = true;
target = collider.gameObject.transform;
// 计算箭与靶的位置偏移
offset = this.transform.position - target.position;
isMove = true;
}
}
// Update is called once per frame
void Update()
{
if (rb.isKinematic == true && isMove == true)
{
this.transform.position = target.position + offset;
}
}
}
7、 游走与碰撞
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Move : MonoBehaviour
{
//public float speed = 1f; // 允许设置速度
public float moveSpeed = 3.0f;
public float rotateSpeed = 90f;
bool canMove = true;
void Start()
{
}
private void OnCollisionEnter(Collision collision)
{
// 阻止角色穿越树木
canMove = false;
Debug.Log("Player collided with a tree and stopped moving!");
}
private void OnCollisionExit(Collision collision)
{
// 允许角色继续移动
canMove = true;
Debug.Log("Player is no longer colliding with a tree and can move again!");
}
void Update()
{
if (canMove)
{
float x = Input.GetAxis("Horizontal");
float y = Input.GetAxis("Vertical");
Vector3 moveDirection = transform.right * x + transform.up * y;
transform.position += moveDirection * Time.deltaTime * moveSpeed;
}
}
}
shejian