实验要求:
实验难点思路:
蓄力拉弓:
通过监听鼠标左键,按下时蓄力,抬起时发射,同时在这期间播放相应的动画,来完 成蓄力拉弓的模型和功能相结合;
碰撞问题:
通过碰撞器的直接识别会出现弓箭打到弓箭和弓箭碰撞到物体后弹开等问题,当我们 的弓箭触碰到非弓箭物体时,冻结该弓箭,以达到弓箭射击后扎入目标的效果,并将 弓箭单独开一个图层,以避免弓箭打到弓箭;
天空盒切换问题:
天空盒的切换直接用按钮实现会出现非常突兀的情况,所以这次实验通过一个 类似于彩蛋的方式来切换;
射击位问题:
通过设置射击位的碰撞器为触发器,使主角不会碰到该碰撞器,而进出时会触发相 应的操作。
定义实验组件:
弓弩:
从unity资源商店下载,有自己的动画器可以调用
箭矢:
将头部作为位置中心,通过代码实现中心在头部的效果,使更贴近真实;头部有专门添加的碰撞器
动态目标:
从unity资源商店下载
静态目标:
从unity资源商店下载,手动添加的复杂的碰撞器
实验代码:
控制核心:
整体场景的总控制者。监听键盘和鼠标的输入,完成射击和移动等动作,限制俯仰角的最大角度,以保证不出现游戏bug;监视主角的移动,以完成各个位置的触发;管理ui,改变ui的显示和关闭。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class 控制核心 : MonoBehaviour
{
public 一号 一;
public 二号 二;
public 目标控制 目标1;
public 目标控制 目标2;
public 目标控制 目标3;
public UnityEngine.UI.Text 静态目标计数器;
public UnityEngine.UI.Text 静态目标计分器;
public UnityEngine.UI.Text 箭矢计数器;
public UnityEngine.UI.Text 动态目标计分器;
public 弓箭 弓箭模板;
public Transform 发射弓箭点;
public Transform 弓箭俯仰;
public Transform 弓箭云台;
public float 蓄力强度;
public float 俯仰速度=.1f;
public float 云台速度=.1f;
public float 移动速度=2;
public AnimationCurve 蓄力曲线;
public Vector3 上一帧鼠标位置;
public Animator 射箭动画;
public Vector3 当前运动角度;
public Vector3 最大运动角度,最小运动角度;
public Vector3 暂存转向;
public KeyCode 前,后,左,右;
public float 箭矢数=0;
public bool 正在射击=false;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if(一.是否射击)
{
静态目标计数器.text = "剩余数量:"+一.个数;
静态目标计分器.text = "得分:"+一.分数;
}
if(!一.是否射击)
{
静态目标计数器.text = "";
静态目标计分器.text = "";
目标1.复活();
目标2.复活();
目标3.复活();
}
if(二.是否射击)
{
动态目标计分器.text = "得分:"+二.分数;
}
if(!二.是否射击)
{
动态目标计分器.text = "";
}
箭矢计数器.text = "箭矢数:"+ 箭矢数;
if(Input.GetKey(前))
{
弓箭云台.Translate(Vector3.forward*Time.deltaTime*移动速度);
}
if(Input.GetKey(后))
{
弓箭云台.Translate(Vector3.back*Time.deltaTime*移动速度);
}
if(Input.GetKey(左))
{
弓箭云台.Translate(Vector3.left*Time.deltaTime*移动速度);
}
if(Input.GetKey(右))
{
弓箭云台.Translate(Vector3.right*Time.deltaTime*移动速度);
}
if(Input.GetKeyDown(KeyCode.Mouse0)&&箭矢数>0)
{
蓄力强度 = 0;
射箭动画.SetBool("Fire",true);
箭矢数--;
正在射击=true;
}
if(Input.GetKey(KeyCode.Mouse0)&&正在射击)
{
蓄力强度 += Time.deltaTime;
}
if(Input.GetKeyUp(KeyCode.Mouse0)&&正在射击)
{
弓箭 暂存 = Instantiate(弓箭模板,发射弓箭点.position,发射弓箭点.rotation);
暂存.发射力 = 蓄力曲线.Evaluate(蓄力强度);
射箭动画.SetBool("Fire",false);
正在射击=false;
}
if(Input.GetKeyDown(KeyCode.Mouse1))
{
上一帧鼠标位置 = Input.mousePosition;
}
if(Input.GetKey(KeyCode.Mouse1))
{
//弓箭俯仰.Rotate(Vector3.right*(上一帧鼠标位置 - Input.mousePosition).y*俯仰速度);
弓箭云台.Rotate(Vector3.down*(上一帧鼠标位置 - Input.mousePosition).x*云台速度);
暂存转向 = Input.mousePosition - 上一帧鼠标位置;
暂存转向 = new Vector3(暂存转向.x,暂存转向.y,0)*俯仰速度;
当前运动角度 += 暂存转向;
if(最大运动角度.y>=360||(当前运动角度.y>=最小运动角度.y&&当前运动角度.y<=最大运动角度.y))
{
弓箭俯仰.Rotate(Vector3.right*(上一帧鼠标位置 - Input.mousePosition).y*俯仰速度);
}
else
{
当前运动角度 -= Vector3.up*暂存转向.y;
}
}
上一帧鼠标位置 = Input.mousePosition;
}
}
弓箭控制:
判定弓箭的生成与发射,并在击中时冻结,并调用射中后目标的反应函数,如扣血和得分等
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class 弓箭 : MonoBehaviour
{
public Rigidbody 自身刚体;
public float 发射力=1000;
public Vector3 上一帧位置;
public bool 未命中物体=true;
public Collider 弓箭头碰撞;
public TrailRenderer 拖尾;
public float 时间;
public void OnCollisionEnter(Collision collision)
{
foreach(ContactPoint fFor in collision.contacts)
{
if(自身刚体.constraints != RigidbodyConstraints.FreezeAll)
{
if(fFor.thisCollider==弓箭头碰撞)
{
if(!collision.gameObject.GetComponent<弓箭>())
{
transform.parent = collision.transform;
自身刚体.constraints = RigidbodyConstraints.FreezeAll;
未命中物体=false;
}
if(collision.gameObject.GetComponentInParent<目标控制>())
{
collision.gameObject.GetComponentInParent<目标控制>().受伤(发射力);
}
if(collision.gameObject.GetComponentInParent<目标控制2>())
{
collision.gameObject.GetComponentInParent<目标控制2>().得分();
}
if(collision.gameObject.GetComponentInParent<天空盒切换器>())
{
collision.gameObject.GetComponentInParent<天空盒切换器>().切换();
}
}
}
}
}
// Start is called before the first frame update
void Start()
{
自身刚体.AddForce(transform.forward* 发射力);
}
// Update is called once per frame
void Update()
{
if(未命中物体)
{
if(transform.position != 上一帧位置)
{
transform.forward = transform.position - 上一帧位置;
}
上一帧位置 = transform.position;
}
if(!未命中物体)
{
this.时间 += (Time.deltaTime);
if(时间>=1)
{
this.拖尾.enabled = false;
}
}
}
}
目标控制:
动态目标控制:
播放移动动画,并管理射中后得分
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class 目标控制2 : MonoBehaviour
{
public 二号 二;
public float 分值;
public void 得分()
{
二.分数 += 分值;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
静态目标管理:
播放受击动画,并管理射中后得分并扣除血量
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class 目标控制 : MonoBehaviour
{
public 一号 一;
public float 最大生命值=100;
public float 当前生命值=100;
public Animator 目标受伤动画控制器;
public GameObject 血条;
public Transform 血条的血量;
public bool 死亡=false;
public float 时间;
public Collider 身体碰撞;
public void 受伤(float 伤害)
{
当前生命值 = Mathf.Clamp(当前生命值 - 伤害,0,最大生命值);
血条.SetActive(当前生命值<=最大生命值);
血条的血量.localScale = new Vector3(1,当前生命值/最大生命值,1);
目标受伤动画控制器.Play("pushed");
if(当前生命值<=0&&死亡==false)
{
死亡=true;
this.身体碰撞.enabled = false;
一.个数--;
一.分数+=100;
}
if(当前生命值<=伤害)
{
一.分数+=当前生命值;
}
if(当前生命值>伤害)
{
一.分数+=伤害;
}
}
public void 复活()
{
死亡=false;
当前生命值=最大生命值;
血条.SetActive(false);
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
射击位控制:
一号射击位:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class 一号 : MonoBehaviour
{
public 控制核心 核心;
public float 个数;
public float 分数;
public bool 是否射击=false;
public void OnTriggerEnter()
{
个数 = 3;
开始射击();
}
public void OnTriggerExit()
{
分数 = 0;
停止射击();
}
public void 开始射击()
{
核心.箭矢数 = 10;
是否射击 = true;
}
public void 停止射击()
{
核心.箭矢数 = 0;
是否射击 = false;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
二号射击位:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class 二号 : MonoBehaviour
{
public 控制核心 核心;
public float 分数;
public bool 是否射击=false;
public void OnTriggerEnter()
{
开始射击();
}
public void OnTriggerExit()
{
分数 = 0;
停止射击();
}
public void 开始射击()
{
核心.箭矢数 = 10;
是否射击 = true;
}
public void 停止射击()
{
核心.箭矢数 = 0;
是否射击 = false;
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
天空盒切换器:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class 天空盒切换器 : MonoBehaviour
{
public Material[] 天空盒合集;
public Light 太阳;
public int index;
public Animator 移动一控制;
public Animator 移动二控制;
public bool 是否白天=true;
public void 切换()
{
RenderSettings.skybox = 天空盒合集[index];
index++;
index %= 天空盒合集.Length;
if(是否白天)
{
移动一控制.Play("移动1");
移动二控制.Play("移动2");
是否白天=false;
太阳.enabled = false;
}
else
{
移动一控制.Play("返回1");
移动二控制.Play("返回2");
是否白天=true;
太阳.enabled = true;
}
}
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
演示成果:
效果图展示: