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