Snap Seting 控制一次移动的步长,按住control 在移动工具中,一般设置为物体的长宽,批量复制
地形设计(Terrain)
笔刷
shift + mouse是降低
四种灯光
直射光
E.G
火堆
使用Lightmapping 贴图节约 性能
添加Lighting选项,将场景和灯光都选择为static
选择所有灯光Mode改为baked
Shadow Type选择 Hard 或者柔和的
粒子系统
火焰
导航系统
添加导航系统
对地形包括灯光所有选择为static
对象设置为不可到达(添加障碍物)
Inspector面板取消勾选对象的 Navigation static(取消障碍物)
烘培完成
蓝色是可行走区域
添加玩家
为角色添加 Navi mesh agent
给玩家添加导航脚本
void Update()
{
//使用射线检测
if (Input.GetMouseButton(0))
{
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//屏幕坐标转换成射线
RaycastHit hit; //保持碰撞信息
if(Physics.Raycast(ray, out hit))
{
Debug.Log(hit.point);
agent.SetDestination(hit.point);//导航
}//射线检测
}
}
控制相机跟随(视野问题)
public class FollowTarget : MonoBehaviour
{
public Transform playertransform;
private Vector3 offset;
// Start is called before the first frame update
void Start()
{
offset = transform.position - playertransform.position;//设置相机跟随距离
}
// Update is called once per frame
void Update()
{
transform.position = playertransform.position + offset;//相机跟随
}
}
把角色在检视窗口赋值给playertransfrom
注意:点击后 人物移动的速度 符合牛顿定律(第1 第2)
TileMap画板使用(2D)
Hirarchy创建
然后创建调色板,拖入Sprite图
鼠标点击单元图,进行粉刷
切割精灵图制作瓦片集
- 选择精灵图集,设置type = multipe类型
- 选择切割方式,Apply,然后拖入调色板 ,灵活配图
Edit按下是对调色板中的瓦片进行操作,抬起是对scene(上方)进行操作
渲染顺序
实现ruby在盒子下方时脑袋挡住盒子,在盒子上方(后面)被挡住腿,远近的实现
细节
设置盒子的Pivot
刚体
- 尽量使用刚体控制移动,不然移动脚本可能造成接触物体发生抖动
- 2D记得冻结Y轴旋转,不然碰撞后发生 侧旋
场景碰撞器
为Tilemap添加tilemap collider 2D
更改Tile图片的的碰撞类型为None ,河流 石头 是sprite
再更改Rigidbody2d 为static
敌人
伤害区域
ctrl + k f 格式化代码
在chactrar 文件夹里设置敌人的 Pivot 为脚部
调整collider的大小为脚部
敌人的移动控制脚本
- 先创建Animator Controller ,挂载到GO上
- 再创建Animation,
编辑精灵图的动画
导入精灵图集的单帧动作动画,调整间隔和顺序
编辑动画之间的间隔
状态机连线,添加条件
在脚本中,设置相应参数
使用混合树
玩家
玩家控制脚本
private Vector2 lookDirection = new Vector2(1,0);//Ruby朝向向量
void Update(){
//玩家输入监听
float horizontal = Input.GetAxis("Horizontal");
float vertical = Input.GetAxis("Vertical");
Vector2 move = new Vector2(horizontal,vertical);
if (!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0))
{
//lookDirection.Set(move.x,move.y);
lookDirection = move;
lookDirection.Normalize();
if (!walkAudioSource.isPlaying)
{
walkAudioSource.clip = walkSound;
walkAudioSource.Play();
}
}
else
{
walkAudioSource.Stop();
}
//动画的控制
animator.SetFloat("Look X",lookDirection.x);//Look X ,Look Y混合树参数
animator.SetFloat("Look Y",lookDirection.y);
animator.SetFloat("Speed",move.magnitude);
//移动
Vector2 position = transform.position;
//Ruby的目标位置
position += speed * move * Time.deltaTime;
//transform.position = position;
//Ruby移动到目标位置
rigidbody2d.MovePosition(position);
}
子弹脚本发射
#Ruby脚本中 动态创建子弹对象
GameObject projectileObject = Instantiate(projectilePrefab,
rigidbody2d.position+Vector2.up*0.5f,Quaternion.identity);//克隆方法
Projectile projectile= projectileObject.GetComponent<Projectile>();//获取子弹脚本组件
projectile.Launch(lookDirection,300);//调用子弹脚本创建一个子弹
animator.SetTrigger("Launch");
//按键绑定
if (Input.GetKeyDown(KeyCode.H))
{
Launch();
}
#Bullet脚本中
public void Launch(Vector2 direction,float force) //子弹发射方法(Ruby调用)
{
rigidbody2d.AddForce(direction*force);//自身刚体调用自己的施加力的方式
}
private void OnCollisionEnter2D(Collision2D collision)
{
//Debug.Log("当前子弹碰撞到的游戏物体是:"+collision.gameObject);
EnemyController enemyController = collision.gameObject.
GetComponent<EnemyController>();
if (enemyController!=null)
{
enemyController.Fix();
}
Destroy(gameObject);//子弹销毁
}
private void Update()
{
if (transform.position.magnitude>100) //子弹不触碰,长距离销毁 transform.position.magnitude 距离向量的模长
{
Destroy(gameObject);
}
}
添加子弹交互动画
通过层级系统 筛选碰撞单元
添加层级
粒子系统
给对象添加粒子系统
更改参数
上传特效图片,选择Start Frame区间,下拉列表,随机在两个之间
血条
使用MASK 制作血条
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UIHealthBar : MonoBehaviour
{
public Image mask;
private float originalSize;
public static UIHealthBar instance { get; private set; }//单例模式,可全局访问
public bool hasTask;
//public bool ifCompleteTask;
public int fixedNum;
private void Awake() //单例模式的实例化
{
instance = this;
}
// Start is called before the first frame update
void Start()
{
originalSize = mask.rectTransform.rect.width;
}
// Update is called once per frame
void Update()
{
}
/// <summary>
/// 血条UI填充显示
/// </summary>
/// <param name="fillPercent">填充百分比</param>
public void SetValue(float fillPercent)
{
mask.rectTransform.SetSizeWithCurrentAnchors(RectTransform.
Axis.Horizontal,originalSize*fillPercent);
}
}
射线检测
NPC对话系统
if (Input.GetKeyDown(KeyCode.T))
{
RaycastHit2D hit = Physics2D.Raycast(rigidbody2d.position+Vector2.up*0.2f,//添加V2.up原因是角色初始锚点在脚部,设置后射线初始位置在胸口
lookDirection,1.5f,LayerMask.GetMask("NPC"));
//存储触碰物体的信息
if (hit.collider!=null)
{
NPCDialog npcDialog = hit.collider.GetComponent<NPCDialog>();//获取NPC身上控制对话的脚本
if (npcDialog!=null)
{
npcDialog.DisplayDialog();
}
}
}
在对话框脚本中使用计时器,确定对话消失时间
void Update()
{
if (timerDisplay>=0)
{
timerDisplay -= Time.deltaTime;
if (timerDisplay<0)
{
dialogBox.SetActive(false);
}
}
}
public void DisplayDialog() //显示当前对话框
{
timerDisplay = displayTime;
dialogBox.SetActive(true);//激活对话框
UIHealthBar.instance.hasTask = true;
if (UIHealthBar.instance.fixedNum>=5)
{
//已经完成任务,需要修改对话框内容
dialogText.text = "哦,伟大的Ruby,谢谢你,你真的太棒了!";
if (!hasPlayed)
{
audioSource.PlayOneShot(completeTaskclip);
hasPlayed = true;
}
}
}
对话框画板 设置为世界模式
血条 和 玩家信息画布应该是Overlay模式
Canvas子对象如果填充Canvas,在锚点视图,点ctrl,选择右下角全充满
子对象选择Image导入对话框背景图
背景音乐与音效
AudioClip(音频剪辑资源)
播放条件需要一个Audio Source
需要一个对象有Audio Listener(一般为相机)
AudioSource组件
public void PlaySound(AudioClip audioClip)//播放音效
{
audioSource.PlayOneShot(audioClip);//PlayOneShot表示只播放一次
}
if (!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0))//Approximately()判断浮点数是否近似相等
{
//lookDirection.Set(move.x,move.y);
lookDirection = move;
lookDirection.Normalize();
if (!walkAudioSource.isPlaying) //移动音效
{
walkAudioSource.clip = walkSound;
walkAudioSource.Play();
}
}
else
{
walkAudioSource.Stop();
}
人物落地音效实现
干扰:上半身碰撞地形/地面行走
- Jump_Time是人物一次起跳后在空中停留的时间
- 引入计时器,玩家起跳后开始计时Clock_Time +=Time.deltaTime
- 接触地形后,判断Clock_Time与Jump_Time关系,测试,选取最小阈值【e.g.起跳上地形上,接触时间可能比较小】
- Clock_Time大于最小阈值判断为落地
- 播放
private AudioClip landClip;
landClip = Resources.Load<AudioClip>("Gris/Audioclips/Land");//落地音效
AudioSource.PlayClipAtPoint(landClip,transform.position);