Unity实例.003官方示例Survival Shooter Tutorial核心代码学习
来源:互联网 发布:淘宝联盟提现手续费 编辑:程序博客网 时间:2024/05/18 01:17
【学习项目为Unity官方的Survival Shooter tutorial,可以在Asset Store下载】
【参考官方学习教程Unity.learn.Survival Shooter tutorial】
Unity官方教学示例《噩梦射手》(Survival Shooter Tutorial)的一些核心代码的学习,包括如何实现角色的移动,摄像机的跟随,敌人的寻路,玩家的生命控制等等内容。
1.控制角色移动:脚本PlayerMovement
创建地板用于射线捕捉,把Layer设定为Floor
public float speed = 6f;// 定义角色的移动速度
Vector3 movement;// 角色的移动向量
Animator anim; // 角色动画组件
Rigidbody playerRigidbody; //角色刚体组件
int floorMask; // 地板遮罩层用于摄像捕捉
float camRayLength = 100f; //从摄像机到屏幕里的射线长度
void Awake ()
{
floorMask = LayerMask.GetMask ("Floor");// 创建一个地板的遮罩层
anim = GetComponent <Animator> ();//引用Player的组件Animator
playerRigidbody = GetComponent <Rigidbody> ();//引用Player的组件Rigidbody
}
LayerMask 层蒙版
struct in UnityEngine
LayerMask.GetMask
无论是内置的层名称集或是由用户在“标签和层管理器”中定义的层,返回与它们相等的层蒙版。
Component 组件
游戏对象的组件的基类
Component.GetComponent 获取组件
如果游戏对象有附加type类型的组件,则返回,如果没有则为空。
射线检测是否和地面碰撞,并且让角色旋转
void Turning ()
{
Ray camRay = Camera.main.ScreenPointToRay (Input.mousePosition);//获取摄像机和鼠标指针所在点连成的线
RaycastHit floorHit;//射线命中点
if(Physics.Raycast (camRay, out floorHit, camRayLength, floorMask))//射线是否命中地板遮罩层
{
Vector3 playerToMouse = floorHit.point - transform.position;//获取向量从角色位置指向射线命中地板的点
playerToMouse.y = 0f;//确保这个向量始终在地板平面上
Quaternion newRotatation = Quaternion.LookRotation (playerToMouse);//创建一个注视旋转,从垂直上方看着地板上向量playerToMouse的沿着Y轴的旋转轴角
playerRigidbody.MoveRotation (newRotatation);//使Player根据newRotatation这个轴角进行旋转
}
}
Camera.main
第一个启用的被标记为“MainCanmera”的相机(只读)。
Camera.ScreenPointToRay
返回一条射线从摄像机通过一个屏幕点。
Input.mousePosition
在屏幕坐标空间当前鼠标的位置(只读)。
RaycastHit
该结构用来获取射线投射返回的信息。
RaycastHit.point
在世界坐标空间,射线碰到碰撞器的接触点。
Physics.Raycast
在场景中投下可与所有碰撞器碰撞的一条光线。
Quaternion.LookRotation
创建一个旋转,沿着forward(z轴)并且头部沿着upwards(y轴)的约束注视。也就是建立一个旋转,使z轴朝向view y轴朝向up。
返回计算四元数。如果用于定向的变换,Z轴将会被对准前方并且如果这些向量正交,Y轴向前。如果forward方向是0,记录一个错误。
Rigidbody.MoveRotation
旋转刚体到新角度。
捕捉输入的h和v,移动角色
void Move (float h, float v)
{
movement.Set (h, 0f, v);//根据键盘的输入设置角色的移动向量
movement = movement.normalized * speed * Time.deltaTime;//对键盘的输入向量进行单位化,给它一个速度和帧时间来得到一帧移动的向量;
playerRigidbody.MovePosition (transform.position + movement);//用Player的当前坐标加上movement的坐标得到目标位置坐标,移动Player到目标位置
}
Vector3.Set
设置现有的Vector3的x、y、z组件
Vector3.normalized
返回向量的长度为1(只读);
当归一化后,向量保持同样的方向,但是长度变为1.0;
注意,当前向量不能改变,而是返回一个新的归一化的向量。如果你想归一化当前向量,使用Normalize函数;
如果这个向量太小而不能被归一化,一个零向量将会被返回。
Time.deltaTime
以秒计算,完成最后一帧的时间(只读);
使用这个函数使你的游戏帧速率独立;
如果你加或减一个每帧改变的值,你应该与Time.deltaTime相乘。当你乘以Time.deltaTime实际表示:每秒移动物体10米,而不是每帧10米;
当从MonoBehaviour的FixedUpdate里调用时,返回固定帧速率增量时间(fixedDeltaTime)。
Rigidbody.MovePosition
移动刚体到新位置;
使用Rigidbody.MovePosition来移动刚体,带有刚体插值设置;
如果刚体插值启用,调用Rigidbody.MovePosition导致在任意两帧之间平滑过渡。如果你想在每固定更新连续移动刚体使用这个;
如果你想把刚体从一个位置瞬移到另一个位置,中间不带过渡,使用Rigidbody.position替代。
如果角色移动就播放移动动画
void Animating (float h, float v)
{
bool walking = h != 0f || v != 0f;//创建一个布尔值,当键盘输入不为0时,即角色移动时,布尔值为真,否则为假;
anim.SetBool ("IsWalking", walking);//对Player的Animator组件中的条件字段赋值,进行真假判断,来执行角色移动动画或者放置动画;
}
Animator 动画器
控制Mecanim动画系统的接口
Animator.SetBool
设置一个布尔参数的值
FiexdUpdate输入Input,并且统一调用以上几个方法
void FixedUpdate ()
{
float h =CrossPlatformInputManager.GetAxisRaw("Horizontal"); // 存储键盘输入的水平坐标
float v = CrossPlatformInputManager.GetAxisRaw("Vertical");// 存储键盘输入的垂直坐标
Move (h, v);//移动Player
Turning ();//使Player跟随鼠标指针转向
Animating (h, v);//Player的动画控制
}
CrossPlatformInput 跨平台输入工具包
2.控制摄像机跟随玩家:脚本CameraFollow
public classCameraFollow: MonoBehaviour {
public Transform target; //用于编辑器中绑定玩家
public float smoothing = 5f; //用于计算顺滑度
Vector3 offset;
void Start() {
//首先初始化的时候保存相机和玩家的相对位置
offset = transform.position - target.position;
}
//这里不要使用FixedUpdate, 移动端屏幕会有视觉卡顿
void Update () {
//计算出相机跟随的位置
Vector3 targetCamPos = target.position + offset;
//设置相机的位置,这里用到了Vector3.Lerp,是一个差值计算,使得移动更柔和.但是会略微消耗计算量
//由于主摄像机只有1个,所以可以忽略这个计算量的消耗
transform.position = Vector3.Lerp (transform.position, targetCamPos, smoothing * Time.deltaTime);
}
}
Vector3.Lerp
两个向量之间的线性插值。
按照分数t在from到to之间插值。这是最常用的寻找一点沿一条线的两个端点之间一些分数的方式(例如,在那些点之间逐渐移动一个对象)。这分数是在范围[ 0…1]。t是夹在 [0…1]之间,当t = 0时,返回from,当t = 1时,返回to。当t = 0.5 返回from和to的中间点。
3.创建一个敌人,自动寻路跟随玩家:脚本EnemyMovement
public class EnemyMovement : MonoBehaviour
{
Transform player; // 定义引用Player位置
PlayerHealth playerHealth; // 定义引用脚本PlayerHealth
EnemyHealth enemyHealth; // 定义引用脚本EnemyHealth
UnityEngine.AI.NavMeshAgent nav; // 定义引用NavMeshAgent
void Awake ()
{
player = GameObject.FindGameObjectWithTag ("Player").transform; //Player的位置坐标
playerHealth = player.GetComponent <PlayerHealth> (); //获取脚本组件PlayerHealth
enemyHealth = GetComponent <EnemyHealth> ();//获取脚本组件EnemyHealth
nav = GetComponent <UnityEngine.AI.NavMeshAgent> ();//获取寻路导航
}
void Update ()
{
if(enemyHealth.currentHealth > 0 && playerHealth.currentHealth > 0)//如果enemy和player的当前血量大于0
{
nav.SetDestination (player.position);//通过寻路导航设置目标点位玩家所在位置;
}
else
{
// ... disable the nav mesh agent.
nav.enabled = false; //如果enemy和player其中之一的血量小于等于0,则关闭寻路导航;
}
}
}
GameObject.FindGameObjectWithTag
返回具体tag标签的激活的游戏对象列表,如果没有找到则为空。
GameObject.transform
获取附加于这个游戏对象上的transform,如果没有则为空。
GameObject.GetComponent
如果这个游戏对象附件了一个类型为type的组件,则返回该组件,否则为空;
GetComponent是访问别的组件的原始方法,脚本的类型就是项目视图里面看到的脚本名称。通过这个函数,你可以访问内置组件或脚本。
NavMeshAgent 导航网格代理
NavMeshAgent.SetDestination 设置目的地
设置或者更新目的地因此触发计算新的路径;
注意该路径可能不会变成可获取的直到一些帧之后。当路径被计算出,pathPending 将会是true。如果一个有效路径变得可获取,那么代理将会重新恢复运动。
NavMeshAgent继承自Behaviour
Behaviour.enabled 启用
启用行为将被Updated,禁用行为将不进行Updated;
行为在检视面板显示为小的复选框。
4.对玩家的生命进行控制:脚本PlayerHealth
public int startingHealth = 100;//Player的初始血量100
public int currentHealth; //定义Player的当前血量变量
public Slider healthSlider; //定义引用血条参数
public Image damageImage; //引用受伤时的图像
public AudioClip deathClip;//添加角色死亡的音效
public float flashSpeed = 5f;//设定damageImage的淡出屏幕的速度
public Color flashColour = new Color(1f, 0f, 0f, 0.1f); //设定damageImage的颜色
Animator anim;//引用动画组件参数
AudioSource playerAudio;//引用音效组件参数
PlayerMovement playerMovement;//引用脚本PlayerMovement
PlayerShooting playerShooting;//引用脚本PlayerShooting
bool isDead; //声明bool值判断角色是否死亡
bool damaged;//声明bool值判断角色是否受伤
void Update ()
{
if(damaged)
{
damageImage.color = flashColour;//角色受伤时,将flashColour赋值给damageImage的color;
}
else
{
damageImage.color = Color.Lerp (damageImage.color, Color.clear, flashSpeed * Time.deltaTime);//通过Lerp,将受伤后屏幕显示的颜色渐变消除
}
damaged = false;//重置角色的受伤状态为否;
}
Color.Lerp 线性插值
颜色a和颜色b之间的线性差值t;
t是夹在0到1之间,当t为0时返回a,当t为1时返回b。
public void TakeDamage (int amount) { damaged = true;//改变角色受伤状态为真 currentHealth -= amount;//当前血量减少,减少值为受到的伤害值 healthSlider.value = currentHealth;//将当前血量赋值给血条 playerAudio.Play ();//播放角色受伤的音效,引用的是Player的音效组件中的Player Hurt if(currentHealth <= 0 && !isDead) { Death ();//当Player的血量小于等于0,isDead为flase时; } }
AudioSource.Play
播放音频剪辑。
void Death ()
{
isDead = true;//Player判定为死亡
playerShooting.DisableEffects ();//调用playerShooting脚本中的DisableEffects方法关闭射击特效
anim.SetTrigger ("Die");//通过触发器播放Player的死亡动画
playerAudio.clip = deathClip;//音效组件引用脚本中的音效deathClip,Player Hurt的音效会停止播放
playerAudio.Play ();//播放音效组件中的deathClip音效
playerMovement.enabled = false;//关闭脚本PlayerMovemonet
playerShooting.enabled = false;//关闭脚本PlayerShooting
}
public void RestartLevel ()
{
SceneManager.LoadScene (0);//重载当前的场景
}
Animator.SetTrigger 设置触发器
设置一个要激活的触发器参数;
触发器是参数,大多数行为像布尔,但当它们被用在过渡时,重置为无效。
SceneManager 场景管理 class in UnityEngine.SceneManagement
运行时的场景管理方法。
SceneManager.LoadScene 加载场景
通过在Build Settings中它们的名称或索引加载场景;
指定场景名称可以是路径的最后部分不加.unity扩展名或者全部路径不加.unity扩展名。该路径在 Build Settings窗口中被精确的显示出来。如果场景名是指定的将会加载匹配到的首个场景。如果有多个名称相同但是路径不同的场景,你应该使用全部路径。
5.控制怪物进行攻击:脚本EnemyAttack
public float timeBetweenAttacks = 0.5f;//攻击的间隔时间
public int attackDamage = 10; //攻击的伤害值
Animator anim; //声明引用动画的参数
GameObject player; //声明引用游戏对象的参数
PlayerHealth playerHealth;//声明引用脚本PlayerHealth的参数
EnemyHealth enemyHealth; //声明引用脚本EnemyHealth的参数
bool playerInRange;//定义角色是否在被攻击范围的布尔值
float timer;//定义下次攻击的计时器
void Awake ()
{
// Setting up the references.
player = GameObject.FindGameObjectWithTag ("Player");//通过标签设置引用的游戏对象为Player
playerHealth = player.GetComponent <PlayerHealth> ();//引用脚本PlayerHealth
enemyHealth = GetComponent<EnemyHealth>();//引用脚本EnemyHealth
anim = GetComponent <Animator> ();//引用游戏动画组件
}
6.控制玩家进行攻击:脚本PlayerShooting
void Update ()
{
timer += Time.deltaTime;//tiemr计时器
if(Input.GetButton ("Fire1") && timer >= timeBetweenBullets && Time.timeScale != 0)
{
Shoot ();//输入开火键 且 计时器大于射击间隔时间 且 timeScale传递时间不为0时 进行射击
}
if(timer >= timeBetweenBullets * effectsDisplayTime) //计时器大于等于射击间隔时间*特效显示时间时
{
DisableEffects ();//关闭射击特效
}
}
void Shoot ()
{
timer = 0f;//射击时计时器重置
gunAudio.Play ();//播放设计音效
gunLight.enabled = true;//光照特效开启
gunParticles.Stop ();//粒子特效关闭
gunParticles.Play ();//粒子特效开启
gunLine.enabled = true;//线渲染器开启
gunLine.SetPosition (0, transform.position);//设置线段起始点为枪所在位置
shootRay.origin = transform.position;//射线起始点为枪的坐标
shootRay.direction = transform.forward;//射线的方向枪的正前方
if(Physics.Raycast (shootRay, out shootHit, range, shootableMask))//射线与射线层内的碰撞器有交集时为真
{
EnemyHealth enemyHealth = shootHit.collider.GetComponent <EnemyHealth> ();//获取射击点所在碰撞器物体的组件的血量脚本
if(enemyHealth != null)//脚本存在时
{
enemyHealth.TakeDamage (damagePerShot, shootHit.point);//射击点所在的敌人执行受伤的方法
}
gunLine.SetPosition (1, shootHit.point);//脚本不存在时,设置线段目标点为射击位置
}
else
{
gunLine.SetPosition (1, shootRay.origin + shootRay.direction * range);//射线与可射击层无交集时,设置线段目标点为为枪所在位置加上射线的长度范围
}
}
Ray.origin 射线的原点
Ray.direction 射线方向,方向总是归一化的向量。如果你指定一个非单位长度的向量,它将被归一化。
LineRenderer 线性渲染
该线性渲染用于在3D空间中绘制独立线条;
该类是线性渲染器组件的脚本接口。
LineRenderer.SetPosition
在线条上设置线条的顶点位置。
7.添加怪物的生命组件:脚本EnemyHealth
using UnityEngine;
public class EnemyHealth : MonoBehaviour
{
public int startingHealth = 100;//敌人初始血量
public int currentHealth;//敌人当前血量
public float sinkSpeed = 2.5f;//敌人死亡后下沉速度
public int scoreValue = 10;//消灭敌人的分数值
public AudioClip deathClip;//敌人死亡音效
Animator anim;//引用动画组件参数
AudioSource enemyAudio;//引用音效参数
ParticleSystem hitParticles;//引用粒子特效系统参数
CapsuleCollider capsuleCollider;//引用胶囊体参数
bool isDead;//敌人是否死亡布尔值
bool isSinking;//敌人是否在下沉布尔值
void Awake ()
{
anim = GetComponent <Animator> ();//获取动画组件
enemyAudio = GetComponent <AudioSource> ();//获取音效组件
hitParticles = GetComponentInChildren <ParticleSystem> ();//获取子对象粒子特效
capsuleCollider = GetComponent <CapsuleCollider> ();//获取组件胶囊体
currentHealth = startingHealth;//当前生命为初始生命
}
void Update ()
{
if(isSinking)
{
transform.Translate (-Vector3.up * sinkSpeed * Time.deltaTime);//下沉为真值时,使敌人向下移动
}
}
public void TakeDamage (int amount, Vector3 hitPoint)
{
if(isDead)
return;//死亡时,不再受伤
enemyAudio.Play ();//播放受伤音效
currentHealth -= amount;//当前血量减去受到的伤害值
hitParticles.transform.position = hitPoint;//把射击点坐标赋值给粒子特效位置
hitParticles.Play();//播放粒子特效
if(currentHealth <= 0)
{
Death ();//敌人生命小于等于0时,敌人死亡
}
}
void Death ()
{
isDead = true;//死亡布尔值为真
capsuleCollider.isTrigger = true;//使胶囊体触发器为真
anim.SetTrigger ("Dead");//动画触发器“Dead”启动
enemyAudio.clip = deathClip;//将deathClip赋值给声音源
enemyAudio.Play ();//播放deathClip
}
public void StartSinking ()
{
GetComponent <NavMeshAgent> ().enabled = false;//寻路导航关闭
GetComponent <Rigidbody> ().isKinematic = true;//刚体受力影响为真
isSinking = true;//下沉状态为真
ScoreManager.score += scoreValue;//分数计数增加
Destroy (gameObject, 2f);//2秒后摧毁当前游戏对象
}
}
8.创建怪物生成器:脚本EnemyManager
using UnityEngine;
using System.Collections;
public class EnemyManager: MonoBehaviour {
public PlayerHealth playerHealth; //玩家的血量
public GameObject enemy; //要创建的怪物
public float spawnTime = 3f; //创建间隔时间
public Transform[] spawnPoints; //创建位置
void Start () {
//循环调用("方法名", 初始等待时间, 循环间隔时间)
InvokeRepeating ("Spawn", spawnTime * 0.7f, spawnTime);
}
void Spawn() {
if(playerHealth.currentHealth <= 0) {
return;
}
//随机得到数组范围内的一个整数
int spawnPointIndex = Random.Range (0, spawnPoints.Length);
//实例化一个物件(物件, 位置, 旋转角度);
Instantiate (enemy, spawnPoints [spawnPointIndex].position, spawnPoints [spawnPointIndex].rotation);
}
}
MonoBehaviour
MonoBehaviour是每个脚本的基类。
MonoBehaviour.InvokeRepeating 重复调用
在time秒调用methodName方法;
简单说,根据时间调用指定方法名的方法;
从第一次调用开始,每隔repeatRate时间调用一次。
Object.Instantiate
克隆原始物体并返回克隆物体。
克隆原始物体,位置设置在position,设置旋转在rotation,返回的是克隆后的物体。这实际上在Unity和使用复制(ctrl+D)命令是一样的,并移动到指定的位置。如果一个游戏物体,组件或脚本实例被传入,实例将克隆整个游戏物体层次,以及所有子对象也会被克隆。所有游戏物体被激活。
实例化更多通常用于实例投射物(如子弹、榴弹、破片、飞行的铁球等),AI敌人,粒子爆炸或破坏物体的替代品。
注意,Instantiate(实例化)能克隆Object(物体)任何类型,包含script(脚本)。
0 0
- Unity实例.003官方示例Survival Shooter Tutorial核心代码学习
- 学习Unity官方教程-Survival Shooter tutorialの声明
- Unity学习笔记一 - Survival Shooter Tutorial
- Unity学习笔记二 - Survival Shooter Tutorial
- Unity学习笔记三 - Survival Shooter Tutorial
- Unity学习笔记四 - Survival Shooter Tutorial
- Unity学习笔记五 - Survival Shooter Tutorial
- Unity学习笔记六 - Survival Shooter Tutorial
- Unity学习笔记七 - Survival Shooter Tutorial
- Unity学习笔记八 - Survival Shooter Tutorial
- Unity学习笔记九 - Survival Shooter Tutorial
- unity官方demo学习:Survival Shooter
- Unity2017官方案例Survival Shooter tutorial 浅析学习
- 【Unity开发实战】官方实例SURVIVAL SHOOTER开发心得
- Unity 官方案例 Survival Shooter 复刻版
- Unity学习笔记(四)----Survival Shooter
- Unity学习笔记(五)----Survival Shooter
- unity survival shooter ZSpace
- java 内存模型与线程
- 欢迎使用Markdown编辑器写博客
- 细说分布式数据库的过去、现在与未来
- JavaScript中的宽松相等和严格相等
- hpuoj寻找单身狗
- Unity实例.003官方示例Survival Shooter Tutorial核心代码学习
- Android 手机识别
- HDU 5976 Detachment (逆元)
- 【CodeForces】792D Paths in a Complete Binary Tree
- php 操作 redis 常用方法代码例子
- PHP实现文章定时发布设置
- linux初级学习之系统恢复2-11
- 关于move_uploaded_file()出错的问题
- 异常