赞
踩
这是中山大学软件工程学院2023年3D游戏编程与设计的第七次作业,也欢迎大家学习参考交流
github个人主页: innitinnit (github.com)
这是一个第一人称的射击类小游戏。
玩家需要通过键盘以及鼠标来控制角色射击目标,在有限的射击次数中达到目标分数则获得游戏胜利。游戏中的射击目标分为动态与静态两种,并且规定只有在变换区域后可以进行射击。单次填装后,射击次数为10次。
-移动(wasd)
-蓄力(长按鼠标左键进行拉弓,松开左键进行停止蓄力;根据长按时间长短来决定发射弓箭力度)
-射击(点击鼠标右键)
-进入静态靶区域(数字键1)
-进入动态靶区域(数字2)
-返回初始位置(字母B)
-切换天空样式(数字键4)
1
-Low-Poly Simple Nature Pack:Low-Poly Simple Nature Pack | 3D 风景 | Unity Asset Store
-Fantasy Skybox FREE:Fantasy Skybox FREE | 2D 天空 | Unity Asset Store
-十字弩:Classical Crossbow | 3D 武器 | Unity Asset Store
玩家在游玩过程中可通过按下按键4可进行天空盒的切换。
编写一个SkyBoxController,挂载到场景中的空对象Sky Box中:
- public class SkyBoxController : MonoBehaviour
- {
- public Material[] skyboxes; // 存储多个天空盒材质
-
- private int currentSkyboxIndex = 0; // 当前天空盒的索引
-
- // Update is called once per frame
- void Update()
- {
- // 通过按键4触发天空盒切换
- if (Input.GetKeyDown(KeyCode.Alpha4))
- {
- ChangeSkybox();
- }
- }
-
- void ChangeSkybox()
- {
- currentSkyboxIndex = (currentSkyboxIndex + 1) % skyboxes.Length;
- RenderSettings.skybox = skyboxes[currentSkyboxIndex];
- }
- }
设置参数,此处使用的是
统一使用以下代码,完成箭矢与射击靶的互动:
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- // 靶子的控制器
- public class TargetController : MonoBehaviour // 用于处理靶子的碰撞
- {
- public int RingScore = 0; //当前靶子或环的分值
-
- public ScoreRecorder sc_recorder;
-
- void Start(){
- sc_recorder = Singleton<ScoreRecorder>.Instance;
- }
-
- void Update(){
- }
-
- void OnCollisionEnter(Collision collision) // 检测碰撞
- {
- Transform arrow = collision.gameObject.transform; // 得到箭身
- if(arrow == null) return;
- if(arrow.tag == "arrow"){
- //将箭的速度设为0
- arrow.GetComponent<Rigidbody>().velocity = new Vector3(0,0,0);
- //使用运动学运动控制
- arrow.GetComponent<Rigidbody>().isKinematic = true;
- arrow.transform.rotation = Quaternion.Euler(0, 0, 0); // 使箭的旋转角度为0
- arrow.transform.parent = this.transform; // 将箭和靶子绑定
- sc_recorder.RecordScore(RingScore); //计分
- arrow.tag = "onTarget"; //标记箭为中靶
- }
- }
- }
见5.0中的代码
同上
使用transport()函数,传送后可以进行射击。
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class ArrowController : MonoBehaviour, ISceneController, IUserAction
- {
- public CCShootManager arrow_manager; //箭的动作管理者
- public ArrowFactory factory; //箭的工厂
- public GameObject main_camera; // 主相机
- public ScoreRecorder recorder; // 计分对象
- public GameObject bow; // 弓弩
- public GameObject target1, target2, target3, target4; // 四种不同的靶子和一个巨型靶子
- public GameObject arrow; // 当前射出的箭
- public string message = ""; // 用于显示的信息
- private int arrow_num = 0; // 装载的箭的数量
- public Animator ani; // 动画控制器
- private List<GameObject> arrows = new List<GameObject>(); // 箭的队列
- private List<GameObject> flyed = new List<GameObject>(); // 飞出去的箭的队列
-
- public float longPressDuration = 2.0f; // 设置长按持续时间
- private bool isLongPressing = false; // 是否正在长按
- private float pressTime = 0f; // 长按的时间
- public int state = 0; // 0-普通状态,1-拉弓状态,2-蓄力状态
- public float power = 0f; // 拉弓的力度
-
- //加载箭和靶子
- public void LoadResources()
- {
- main_camera = transform.GetChild(5).gameObject; // 获取摄像机
- bow = this.gameObject;
- // 加载靶子
- for(int i = 0; i < 3; ++i) // 3 * 2 = 12个靶子
- {
- target1 = LoadTarget("target", 10 + i, new Vector3(100+20*i, 20, 140+5*i)); // 静态小靶子
- target2 = LoadTarget("big_target", 5-i, new Vector3(100+20*i, 10, 140-5*i)); // 静态大靶子
- }
- target3 = LoadTarget("target_move", 15, new Vector3(0,0,0)); // 慢速移动靶子, 使用动画,自带位置
- target4 = LoadTarget("target_quick", 20, new Vector3(0,0,0)); // 快速移动靶子,使用动画,自带位置
- }
-
- GameObject LoadTarget(string name, int score, Vector3 pos) // 加载靶子
- {
- GameObject target = GameObject.Instantiate(Resources.Load("Prefabs/"+name, typeof(GameObject))) as GameObject; // 从预制中获得靶子
- target.transform.position = pos;
- target.AddComponent<TargetController>(); // 给靶子添加TargetController组件
- target.GetComponent<TargetController>().RingScore = score;
- return target;
- }
-
- //进行资源加载
- void Start()
- {
- arrow = null;
- SSDirector director = SSDirector.getInstance();
- director.currentSceneController = this;
- director.currentSceneController.LoadResources();
- gameObject.AddComponent<ArrowFactory>();
- gameObject.AddComponent<ScoreRecorder>();
- gameObject.AddComponent<UserGUI>();
- gameObject.AddComponent<BowController>();
- factory = Singleton<ArrowFactory>.Instance;
- recorder = Singleton<ScoreRecorder>.Instance;
- arrow_manager = this.gameObject.AddComponent<CCShootManager>() as CCShootManager;
- ani = GetComponent<Animator>();
- }
-
- void Update()
- {
- Shoot(); // 射击
- HitGround(); // 检测箭是否射中地面
- for(int i = 0; i < flyed.Count; ++i) // 用于维护已经射出去的箭的队列
- {
- GameObject t_arrow = flyed[i];
- if(flyed.Count > 10 || t_arrow.transform.position.y < -10) // 箭的数量大于10或者箭的位置低于-10
- { // 删除箭
- flyed.RemoveAt(i);
- factory.RecycleArrow(t_arrow);
- }
- }
- if(Input.GetKeyDown(KeyCode.R)) // 按R换弹
- { //从工厂得到箭
- LoadArrow();
- message = "弩箭已填充完毕";
- }
- }
-
- public void Shoot() // 射击
- {
- if(arrows.Count > 0){ // 确保有箭
- if(!gameObject.GetComponent<BowController>().canshoot && (Input.GetMouseButtonDown(0) || Input.GetButtonDown("Fire2"))){
- message = "请按1、2到指定地点射击\n按B返回";
- }
- else{
- aniShoot();
- }
- }
- else{
- message = "请按R键以装载弓箭";
- }
- }
-
- public void aniShoot(){ // 射击的动画
- if (Input.GetMouseButtonDown(0) && state==0) // 监测鼠标左键按下
- {
- message = "";
- transform.GetChild(4).gameObject.SetActive(true); // 设置动作中的箭可见
- isLongPressing = true;
- ani.SetTrigger("pull");
- pressTime = Time.time;
- state = 1;
- }
- if (Input.GetMouseButtonUp(0) && state==1) // 监测鼠标左键抬起
- {
- isLongPressing = false;
- float duration = Time.time - pressTime;
- if (duration < longPressDuration){ // 执行普通点击操作
- power = duration/2;
- }
- else{ // 拉满了
- power = 1.0f;
- }
- ani.SetFloat("power", power);
- ani.SetTrigger("hold");
- state = 2;
- }
- if (isLongPressing && Time.time - pressTime > longPressDuration) // 长按但是未抬起,且持续时间超过设定值
- {
- // 长按操作
- isLongPressing = false;
- power = 1.0f;
- ani.SetFloat("power", power);
- ani.SetTrigger("hold");
- }
- if (Input.GetButtonDown("Fire2") && state==2){ // 鼠标右键,攻击2
- transform.GetChild(4).gameObject.SetActive(false); // 设置动作中的箭不可见
- ani.SetTrigger("shoot");
- arrow = arrows[0];
- arrow.SetActive(true);
- flyed.Add(arrow);
- arrows.RemoveAt(0);
- arrow_manager.ArrowShoot(arrow, main_camera.transform.forward,power);
- ani.SetFloat("power", 1.0f); // 恢复力度
- arrow_num -= 1;
- state = 0;
- }
- }
-
- public void LoadArrow(){ // 获得10支箭
- arrow_num = 10;
- while(arrows.Count!=0){ // 清空队列
- factory.RecycleArrow(arrows[0]);
- arrows.RemoveAt(0);
- }
- for(int i=0;i<10;i++){
- GameObject arrow = factory.GetArrow();
- arrows.Add(arrow);
- }
- }
-
- public void HitGround(){ // 检测箭是否射中地面
- RaycastHit hit;
- if (arrow!=null && Physics.Raycast(arrow.transform.position, Vector3.down, out hit, 2f))
- {
- // 如果射线与地面相交
- if (hit.collider.gameObject.name == "Terrain")
- {
- arrow.tag = "ground";
- //将箭的速度设为0
- arrow.GetComponent<Rigidbody>().velocity = new Vector3(0,0,0);
- //使用运动学运动控制
- arrow.GetComponent<Rigidbody>().isKinematic = true;
- }
- }
- }
-
- //返回当前分数
- public int GetScore(){
- return recorder.score;
- }
-
- //得到剩余的箭的数量
- public int GetArrowNum(){
- return arrow_num;
- }
-
- // 显示的信息
- public string GetMessage(){
- return message;
- }
- }
此处使用一个胶囊状3d物体作为玩家控制角色,因为是第一人称视角所以玩家无法看到自己的角色。
这一部分中,我们需要完成:
-角色视角随鼠标移动(CameraController.cs)
-使用键盘可以操控角色移动(PlayerController.cs)
CameraController.cs(挂载至Camera):
- public class CameraController : MonoBehaviour
- {
- public Transform player;//获取玩家旋转的值来进行视角旋转
-
- private float mouseX, mouseY;//获取鼠标移动的值
- public float mouseSensitivity;//鼠标灵敏度
-
- public float xRotation;
-
- private void Update()
- {
- mouseX=Input.GetAxis("Mouse X")*mouseSensitivity*Time.deltaTime;
- mouseY=Input.GetAxis("Mouse Y")*mouseSensitivity*Time.deltaTime;
-
- xRotation -= mouseY;
- xRotation = Mathf.Clamp(xRotation, -70, 70);//使玩家无法无限制地上下旋转视角
-
- player.Rotate(Vector3.up * mouseX);
- transform.localRotation=Quaternion.Euler(xRotation, 0,0);
-
- }
- }
PlayerController.cs(挂载至Player):
- public class PlayerController : MonoBehaviour
- {
- private CharacterController cc;
- public float moveSpeed;
- public float jumpSpeed;
-
- private float horizontalMove, verticalMove;
- private Vector3 dir;
-
- public float gravity;
- private Vector3 velocity;
-
- //用于检测玩家是否在地面上
- public Transform groundCheck;
- public float checkRadius;
- public LayerMask groundLayer;
- public bool isGround;
-
- private void Start()
- {
- cc=GetComponent<CharacterController>();
- }
-
- private void Update()
- {
- isGround=Physics.CheckSphere(groundCheck.position,checkRadius,groundLayer);//玩家是否碰撞地面
- if (isGround && velocity.y < 0)
- {
- velocity.y = -2f;
- }
-
- horizontalMove = Input.GetAxis("Horizontal")*moveSpeed;
- verticalMove = Input.GetAxis("Vertical") * moveSpeed;
-
- dir=transform.forward*verticalMove+transform.right*horizontalMove;
- cc.Move(dir*Time.deltaTime);
-
- //跳跃
- if(Input.GetButtonDown("Jump")&& isGround)
- {
- velocity.y = jumpSpeed;
- }
-
- velocity.y-=gravity*Time.deltaTime;
- cc.Move(velocity*Time.deltaTime);
- }
-
- }
设置参数:
Player:
使用Character Controller组件,使角色具有碰撞体属性。
设置collider即可完成。
见5.0部分代码(record相关部分)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。