2d rubys adventure
unity asset store资源商店:Unity 资源商店 - 优质3D 2D游戏制作资源平台
Ctrl+z撤回;Ctrl+y重做
多行注释Ctrl+k Ctrl+c
解除多行注释Ctrl+k Ctrl+u
Ctrl多选;shift连选
对齐代码Ctrl+k,Ctrl+f
f12查看方法
文章目录
unity认识
面板
可以在windows下选择添加面板
-
项目面板project
资源文件 (右下角上下箭头 》查看详情)
-
输出面板console
提示、报错
-
层次面板hierarchy/场景
游戏物体对象、父子级关系
Ctrl+D复制对象
-
场景演示面板scene view
-
游戏演示面板game view
-
检视面板inspector/属性
对象数据
添加组件(物体必带transform组件)
工具栏
**中间:**运行
- 开始/结束
- 暂停
- 逐帧step
**左边:**操作(快捷键QWERTY)
-
视野查看工具hand(Q)
鼠标拖动视野;滚轮缩放视野
-
移动move(W)
沿着坐标轴移动
-
旋转rotate(E)
-
缩放scale(R)
沿着轴缩放
-
矩形工具Rect Transform(T)
显示白框
可以移动、旋转、缩放(自由)
shift等比例缩放
-
多功能工具(Y)
可以移动、旋转、缩放(沿着轴)
-
其他工具
使用其他工具时,图标变化
(对物体操作=》修改物体属性面板Transform)
聚焦某个物体:选中物体,按下F
拖拽视野:鼠标右键
3d下视角移动:鼠标右键+wsadqe
**右上方:**layout布局模式
推荐使用2by3模式
创建
场景:scenes文件夹
file——new scene
**图片:**sprite精灵(sprite sheet精灵图集)
2d项目中,导入图片直接转成sprite(2D and UI)
图片物体自带sprite renderer组件,用于渲染图片
坐标:
-
对于世界坐标原点的差值(无父对象)
-
对于父对象原点的差值
脚本:Scripts文件夹
create——c# Script
挂载到游戏物体上:拖动脚本/Add component
修改代码后回到unity,需要注意右下角,等待编译完成
命名空间
类(组件)
函数(方法)
start游戏开始调用一次
update每帧调用一次(和性能有关,大约1s60次)
变量类型 变量名
Vector2类型:存储2个数字(x,y)
Vector3类型:存储3个数字(x,y,z)
小数:需要加f(float类型)
= 赋值符号 把右边赋值给左边
Ruby移动
unity输入设置:edit——project setting——input manager
方向控制(asdw)
x轴名称Horizontal
y轴名称Vertical
negative Button负向-1
positive Button正向1
》RubyController
float horizontal=Input.GetAxis("horizontal");//获取-1~1之间的比例系数
Debug.Log(horizontal);//在console面板输出信息
Ruby移动脚本:
-
获取玩家输入Input.GetAxis(“名称”)(与输入设置内的name对应)
-
游戏对象的位置transform.position(2d游戏中看作Vector2变量,实际是Vector3变量、z=0)
-
游戏帧率Application.targetFrameRate(一般不需要设置,通常为30、60)
void Start()
{
//Application.targetFrameRate=30;
//帧率 每1s运行多少次(update)
}
void Update()
{
//获取玩家输入
float horizontal=Input.GetAxis("Horizontal");
float vertical=Input.GetAxis("Vertical");
Vector2 position=transform.position;
//Ruby移动 移动距离=速度*(玩家输入的移动的比例系数)*时间
//根据具体调试情况设置速度 此处设为3
//Time.deltaTime每一帧的时间 大约为1s/60
position.x+=3*horizontal*Time.deltaTime;
position.y+=3*vertical*Time.deltaTime;
transform.position=position;
}
TileMap地图编辑
物体按照固定值移动:w移动模式下,按住Ctrl
edit——grid and snap——move设置移动步长
tilemap类型:
- Rectangle矩形
- hexagon六边形
- isomatric等距菱形
创建tilemap:(使用矩形Rectangle)
(sprite精灵——tile瓦片——palette调色板——brush笔刷——tilemap瓦片地图)
右键2d object——tilemap瓦片地图
可以在同一个grid下创建多个tilemap
- grid网格
- tilemap瓦片
创建Tiles文件夹、tile palette文件夹
打开tile编辑面板window——tile palette
创建palette调色板create new palette,保存在tile palette文件夹内
//创建瓦片2d object——Tile,赋值图片(新版本不知道选哪个类型。。)x
在调色板里直接拖入所需的tile瓦片,保存到tiles文件夹
问题:tile填充不满格子
图片大小小于格子大小
图片信息pixels per unit 每个unit单位包含多少像素点
右下角打开图片,显示图片的真实大小为多少像素
》将pixels per unit改为图片实际像素大小(apply应用)
使用精灵图集:
将单张精灵图片的sprite mode的simple改为multiple
打开图片的sprite editor
选择slice切割类型(apply应用)
- automatic自动识别
- grid by cell size按照大小
- grid by cell count平均分
调色板的工具栏:
-
选择select(s)
多选:鼠标左键框选
场景/调色板内
-
移动move(m)
移动场景中的瓦片
-
画笔paint brush(b)
选择瓦片后,在场景中绘制
-
方形区域填充工具fill box tool(u)
选择区域的瓦片,填充面积小会填充部分瓦片、填充面积大会平铺
-
取色工具picker tool(i)
选择瓦片后,在场景中绘制
-
橡皮擦eraser(d)
擦去场景中的瓦片
橡皮大小可以利用select多选区域
-
填充工具fill tool(g)
填充没有瓦片的区域
edit模式:操作palette调色板内的内容
渲染顺序:
-
深度depth(离相机越近,渲染在上面)(2d游戏中不建议使用 z轴)
-
层次layer(值越大,渲染在上面)randerer组件上的order in layer属性
地形位于最下,将层级变小
丰富游戏世界
自定义渲染轴向:
edit——project settings——graphic——camera settings
transparency sort mode——custome axis
角色移动与立体物体之间关系:角色y小于物体、角色渲染在上面;角色y大于物体、角色渲染在下面
=》xyz设置为(0,1,0)(只与值的正负有关系,一般0、-1、1即可)
(注:需要物体的order in layer相同的情况下,自定义渲染轴向才会生效!)
轴向渲染相关:角色渲染点
游戏物体sprite randerer——sprite sort point
默认为center中心点
需要修改为pivet轴心点(可自定义)(游戏物体坐标即轴心点的坐标)
修改物体的pivot轴心点=》修改原图片
-
修改原图片的pivot(可选择9种自带或者自定义)
-
如果需要自定义,进入sprite editor,控制蓝色点的位置(值在0~1之间)
游戏物体的复用:预制体prefab
创建Prefabs文件夹,拖入场景内的游戏物体,即可制作为预制体
*修改预制体:*open prefab进入预制体编辑模式;双击预制体进入;克隆体右侧小箭头
修改预制体》克隆体随着一起改变
可以选择是否自动保存修改auto save
修改克隆体:直接改变克隆体
将克隆体的修改、应用到预制体上,改变所有克隆体=》
override——apply all
(修改资源文件,无法Ctrl+z撤回操作)
物理系统
rigidbody 2d刚体组件
gravity scale重力比例:
默认为1,即受重力影响,物体会下落》可用于横版侧视图游戏
横版俯视图游戏》gravity scale重力比例 设置为0
box collider 2d碰撞体组件
编辑碰撞器:edit collider
(ruby:脚的部分)
(金属盒:下半部分)
碰撞检测发生条件:
-
双方必须有碰撞器
-
一方有刚体(最好是运动的一方)
-
刚体长期不用会进入休眠状态
问题:Ruby的旋转问题
原理=Ruby的旋转z发生变化
解决:rigidbody 2d组件——constraints——freeze rotation z冻结z轴旋转
问题:Ruby的抖动问题
原理=自定义的脚本与unity自带的物理系统共同作用于Ruby,发生冲突
解决:通过自定义脚本 控制刚体rigidbody 2d移动物体
private Rigidbody2D rigidbody2d;
void Start()
{
rigidbody2d=GetComponent<Rigidbody2D>();//获取刚体组件
}
void Update()
{
//……
//transform.position=position;不再使用Transform的position控制移动
rigidbody2d.MovePosition(position);//刚体组件的移动方法MovePosition
}
静态方法:类名.方法名(参数)
全局可访问,不需要实例
普通方法(成员变量方法):对象名.方法名(参数)
需要实例
地形上的碰撞:
方法1:创建空物体,添加碰撞器组件,编辑碰撞器范围
方法2:在tilemap上添加tilemap collider 2d组件
在tiles文件夹内,选择不需要碰撞的tile——collider type——none
解决碰撞器之间影响问题(缝隙)=》联合碰撞器
在tilemap上添加composite collider 2d组件(必须有rigidbody 2d组件,没有会自动添加)
tilemap collider 2d组件上勾选used by composite
避免地形的移动》rigidbody 2d组件——body type——static静止
道具
血量
public int maxHealth=5;//最大生命值
private int currentHealth;//目前生命值
void Start()
{
//……
currentHealth=maxHealth;
}
public void ChangeHealth(int amount)
{
currentHealth=Mathf.Clamp(currentHealth+amount,0,maxHealth);//血量限制在范围内(值,最小,最大)
//Debug.Log(currentHealth+"/"+maxHealth);
}
访问级别——访问修饰符
-
public共有
-
private私有
-
protected可用于子类
方法默认为private
检视面板控制ruby移动速度:
public int speed=3;
变量的赋值顺序
变量初始化值(start之前)》检视面板》start方法
触发器
box collider 2d——is trigger勾选
触发检测条件
-
两者都有碰撞器box collider
-
有一个为触发器 勾选is trigger
-
运动的一方有刚体rigidbody
血包触发代码
挂载在血包上
进入区域会触发》OnTriggerEnter2D方法
》HealthCollectible
private void OnTriggerEnter2D(Collider2D collision)
//unity自带的触发器方法 参数:触发触发器的物体(ruby)
{
RubyController rubyController=collision.GetComponent<RubyController>();//获取触发物体ruby的组件(脚本)RubyController
//如果触发物体是敌人,则没有RubyController脚本,rubyController为空null
if(rubyController!=null)
//如果rubyController非空
{
if(rubyController.currentHealth<rubyController.maxHealth)
//如果ruby不满血
{
rubyController.ChangeHealth(1);//调用RubyController脚本中的ChangeHealth方法,参数为1
Destroy(gameObject);//在触发后,销毁游戏物体(gameObject->自身->血包)
}
}
}
属性
若外部修改currentHealth,可能出现currentHealth不在规定范围内的问题(0~max)(因为没有经过ChangeHealth方法)
目标:外部可以调用,但是不能改变
》rubyController上定义属性
//可调用,可修改
//public int Health{
// set{health=value;}
// get{return currentHealth;}
//}
//可调用,但是不能改变
public int Health{
get{return currentHealth;}
}
》血包代码上使用
currentHealth改用Health
if(rubyController.Health<rubyController.maxHealth)
{
//无法修改currentHealth 以下代码报错:rubyController.currentHealth+=1;
rubyContoller.ChangeHealth(1);//调用的是rubyController的内部方法,可以修改rubyController的currentHealth
Destroy(gameObject);
}
注:属性不会显示在检视面板中
伤害区域
box collider 2d——is trigger勾选(触发器)
与血包类似,但是减血
在区域内会不断减血》OnTriggerStay2D方法
》DamageZone
private void OnTriggerStay2D(Collider2D collision)
{
RubyController rubyController=collision.GetComponent<RubyController>();
if(rubyController!=null)
{
rubyController.ChangeHealth(-1);
Debug.Log(rubyController.Health);//ruby当前的血量
}
}
无敌时间
》rubyController
//ruby的无敌时间
public float timeInvincible=2.0f;//无敌状态持续时间
private bool isInvincible;//是否为无敌状态
private float invincibleTimer;//计时器
void Update()
{
//……
if(isInvincible)//如果是无敌状态
{
invincibleTimer=timeInvincible-Time.deltaTime;//开启计时器,递减 约1/1s
if(invincibleTimer<=0)//计时器归0
{
isInvincible=false;//结束无敌状态
}
}
}
public void ChangeHealth(int amount)
{
if(amount<0)//如果是减血
{
if(isInvincible)//如果是无敌状态
{
return;//直接退出,不改变血量
}
//受到伤害
isInvincible=true;//受伤后进入无敌状态
invincibleTimer=timeInvincible;//开启计时器
}
//……
}
让刚体不进入睡眠模式
(ruby)——rigidbody2d——sleeping mode——never sleep
增加伤害区域(放大)
(原图片文件)——mesh type——full rect
(Damageable)——sprite renderer——draw mode——tiled平铺
(Damageable)——sprite renderer——tile mode——adaptive
(Damageable)——box collider 2d——auto filling勾选
敌人
(robot)——设置成预设体Prefab
-
box collider 2d碰撞体
edit——设置在脚上
-
rigidbody 2d刚体
——gravity——0
——freeze rotation——z
(原文件)
——pivot轴心点——custom(0.5,0)(位于脚下)
敌人的移动
敌人按间隔时间,往返运动
》EnemyController
public float speed=3;
private Rigidbody2D rigidbody2d;
public bool vertical;//控制轴向
private int direction=1;//控制方向
public float changeTime=3;//方向改变的时间间隔
private float timer;//计时器
void Start()
{
rigidbody2d=GetComponent<Rigidbody2D>();//获取刚体组件
timer=changeTime;//初始化计时器时间,为间隔时间
}
void Update()
{
//计时器
timer-=Time.deltaTime;//计时器随着时间减小
if(timer<0)//如果计时器小于0
{
direction=-direction;//改变方向
timer=changeTime;//重置计时器
}
Vector2 position=rigidbody2d.position;//刚体组件的位置信息
if(vertical)//垂直轴向
{
position.y+=Time.deltaTime*speed*direction;
//位置变化=时间*速度*(方向)
}
else//水平轴向
{
position.x+=Time.deltaTime*speed*direction;
}
rigidbody2d.MovePosition(position);
}
敌人的碰撞检测
ruby与robot碰撞后减血
碰撞器与触发器区别=》方法不同(collision)(trigger)
》EnemyController
private void OnCollisionEnter2D(Collision2D collision)
{
RubyController rubyController=collision.gameObject.GetComponent<RubyController>();
//这里的collision没有getcomponent方法,需要先找gameobject
if(rubyController!=null)
{
rubyController.ChangeHealth(-1);
Debug.Log(rubyController.Health);
}
}
动画系统
动画控制器animator controller(状态机)
创建animations文件夹,创建animator controller类型文件
动画组件animator
controller属性:需要赋值animator controller类型文件
创建动画
打开动画窗口window——animation——animation
窗口内创建animation clip类型资源
1s60帧
【单一图片动画】
add property选择游戏物体
可以更改颜色、大小、旋转等等(inspector面板内的属性)
改变后自动添加关键帧;在时间轴上右键add key添加关键帧
选择preview预览
也可以录制,记录属性的改变
机器人的动画
机器人的左移动画
(实际是改变了精灵渲染组件的sprite属性)
选择相关的图片,拖入动画窗口(注意不要漏)
增加图片的间隔:全选每个关键帧,向右拖箭头,增加间距(大约一共0.35s、0.4s)
机器人的左移动画
需要将图片左右翻转——flip属性(x)
add property——sprite renderer——flip X
机器人的默认状态动画idle
只需要一张图片
减慢播放速度:复制一次关键帧,拖到后面
动画控制器
打开动画控制器窗口window——animation——animator
左:
layers层级
parameter参数,加号添加
(MoveX:float类型)
(Vertical:bool类型)
条件设置的4个参数
- float大于小于
- int大于小于 等于
- bool开关状态
- trigger顺序触发
右:
可拖入clip文件
entry连接默认状态的动画(右键动画——set as layer default state)
动画状态转换
关联动画(右键起始动画——make transition——拖到目标动画)
选择关联箭头,在inspector面板——conditions添加状态转换条件(使用的是左边设置的参数)greater大于,less小于
使用代码,设置状态机的参数
》EnemyController(不使用混合树的情况下)
private Animator animator;
void Start()
{
animator=GetComponent<Animator>();//获取animator组件
animator.SetFloat("MoveX",direction);//MoveX为自定义的动画控制器内的float类型的参数;;direction为脚本内的变量》》设置为MoveX随direction一样
//需要在start内设置一次:如果不写会直接执行if条件内的内容,则游戏前一段时间没有动画
animator.SetBool("Vertical",vertical);
}
void Update()
{
//……
if(timer<0)
{
//direction=-direction;
animator.SetFloat("MoveX",direction);
//timer=changeTime;
}
}
状态直接切换》状态转换的inspector面板设置
has exist time动画转换时,前一个动画继续的时间——取消勾选
transition duration动画过度的时间——设置为0
混合树:通过多个参数、决定播放哪个动画
animator面板右侧空白处——右键——create state——from new blend tree
双击进入混合树
左:添加参数
(MoveX:float类型)
(MoveY:float类型)
右:设置当前状态的坐标点(红点)
inspector面板:
选择混合树的类型,选择参数(1个?或2个?)
+add motion field
给motion设置对应的动画
多个动画会形成坐标系》设置不同动画的xy坐标(坐标系中的点)
当前状态接近哪个坐标,就会播放该坐标的动画
》EnemyController(使用混合树的情况下)
private Animator animator;
void Start()
{
animator=GetComponent<Animator>();//获取animator组件
//animator.SetFloat("MoveX",direction);
//animator.SetBool("Vertical",vertical);
PlayMoveAnimation();
}
void Update()
{
//……
if(timer<0)
{
//...
//animator.SetFloat("MoveX",direction);
PlayMoveAnimation();
//...
}
}
private void PlayMoveAnimation()//控制移动动画的方法
{
if(vertical)//垂直轴向
{
animator.SetFloat("MoveX",0);
animator.SetFloat("MoveY",direction);
}
else//水平轴向
{
animator.SetFloat("MoveX",direction);
animator.SetFloat("MoveY",0);
}
}
ruby的动画
——利用官方提供的animation controller
ruby添加animator组件——设置对应的animation controller
idle状态(混合树):look xy控制ruby的朝向
moving状态(混合树):look xy控制ruby的移动方向动画
hit状态(混合树):不同方向的受击动画(hit——trigger类型参数)
launch状态(混合树):不同方向射击的动画(launch——trigger类型参数)
(同样的参数,但是前提的状态是不一样的)
前提的状态》判断》状态转换的条件=》speed><0.1
ruby动画的代码
》RubyController
private Vector2 lookDirection=new Vector2(1,0);//ruby的朝向 初始为朝右
private Animator animator;
void Start()
{
animator=GetComponent<Animator>();//获取animator组件
}
void Update()
{
//获取玩家输入……
///float horizontal=Input.GetAxis("Horizontal");
///float vertical=Input.GetAxis("Vertical");
Vector2 move=new Vector2(horizontal,vertical);//利用向量存储2个变量 玩家移动的xy
if(!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0))
//Mathf.Approximately方法:判断两个值是否近似相等(小数的存储可能存在误差)
//当前玩家输入的某个轴向值不为0
//玩家x轴输入不为0 或者 玩家y轴输入不为0
{
lookDirection.Set(move.x,move.y);//将玩家输入的方向设置为ruby的朝向 另一种写法:lookDirection=move;
lookDirection.Normalize();//将向量标准化 值都变成1,-1
}
animator.SetFloat("Look X",lookDirection.x);//将脚本的朝向参数 设置成animator的参数
animator.SetFloat("Look Y",lookDirection.y);
animator.SetFloat("Speed",move.magnitude);//速度=>方向 向量的模长
//……
//简化移动的代码 利用向量运算 合并为一句
//position.x+=speed*horizontal*Time.deltaTime;
//position.y+=speed*vertical*Time.deltaTime;
position+=speed*move*Time.deltaTime;
}
子弹
发射子弹
子弹变小:子弹资源文件——pixels per unit——改为300(原100)
添加rigidbody2d组件——重力gravity为0
添加box collider2d组件
物体移动的3种方式
- transition.position
- rigidbody2d.MovePosition(position);
- 受力
子弹的预制体
》Projectile
private Rigidbody2D rigidbody2d;
void Awake()
//在start之前调用
//在物体实例化前马上调用
//不能使用start,因为rigidbody2d会在没有实例化时候被调用》报空
{
rigidbody2d=GetComponent<Rigidbody2D>();
}
public void Launch(Vector2 direction,float force)//发射子弹(力的方向,力的大小)
{
rigidbody2d.AddForce(direction*force);//受力 (力的矢量)
}
private void OnCollisionEnter2D(Collision2D collision)//碰撞检测
{
Debug.Log("当前子弹碰撞到的物体是:"+collision.gameObject);
Destroy(gameObject);//销毁子弹自身
}
ruby的预制体
》RubyController
public GameObject projectilePrefab;
void Update()
{
//...
if(Input.GetKeyDown(KeyCode.H))//玩家输入(键盘按下按键) 发射子弹
{
Launch();
}
}
private void Launch()//ruby发射子弹
{
GameObject projectileObject=Instantiate(projectilePrefab,rigidbody2d.position,Quaternion.identity);//实例化对象 创建克隆体(克隆的母体 一般是预制体,需要生成在的位置,旋转角度)
//四元数类型变量Quaternion.identity 无角度
Projectile projectile=projectileObject.GetComponent<Projectile>();//获取组件
projectile.Launch(lookDirection,300);//调用方法 (力的方向,力的大小)
//动画:animator.SetTrigger("Launch");//Launch为动画的Trigger类型的参数
}
在检视面板赋值projectilePrefab<<子弹的预制体
层级 控制碰撞检测
(指物体右上角的layer;不是renderer里面的order in layer)
添加层级:add ——(Character——放ruby)(Projectile——放子弹)
在预制体上设置层级
设置层级不发生碰撞检测:edit——project settings——physics2d——层级碰撞矩阵——按需要取消勾选(Character x Projectile)(默认层级之间有碰撞检测)
修复敌人
》EnemyController
private bool broken;//机器人是否故障
void Start()
{
broken=true;
}
void Update()
{
if(!broken)//如果机器人修好,停止移动
{
return;
}
//...
}
public void Fix()//修复机器人
{
broken=false;//修好
rigidbody2d.simulated=false;//不再发生碰撞检测
//动画:animator.SetTrigger("Fixed");
}
》Projectile
private void OnCollisionEnter2D(Collision2D collision)//碰撞检测
{
//Debug.Log("当前子弹碰撞到的物体是:"+collision.gameObject);
EnemyController enemyController=collision.gameObject.GetComponent<EnemyController>();//碰撞检测不能直接获取GetComponent,需要先获取物体gameObject
if(enemyController!=null)//判断是否为空
{
enemyController.Fix();//修复机器人
}
Destroy(gameObject);//销毁子弹自身 只有碰到别的物体就会销毁
}
无碰撞时的销毁子弹
》Projectile
void Update()
{
if(transform.position.magnitude>100)//子弹距离屏幕中心原点的长度(模长) 距离远就进行销毁
{
Destroy(gameObject);
}
}
机器人修复动画(跳舞)
选中robot
animation面板:add new Animation clip——拖入图片,调整时间
animator面板:添加trigger类型参数Fixed——连接混合树》fixed——条件为fixed——取消has exit time——transition duration设置为0
cinemachine相机
unity的工具包 可自行选择使用
摄像机跟随:使用工具包 更方便
添加工具包:window——package manager——(未安装的包)cinemachine包——install
添加相机:右键创建cinemachine——2d camera
组件属性:
lens——orthographic size改变视野大小
高度与摄像机size有关(unity单位)
宽度与屏幕尺寸有关
摄像机的渲染模式:主相机——camera——projection(主相机的与其他相机的一致)
- orthographic正交(一般2d)
- perspective透视(一般3d)
摄像机跟随:cinemachine virtual camera组件的follow属性赋值 为摄像机需要跟随的对象(ruby)
摄像机跟随的边界限制:
cinemachine virtual camera组件
add extension——cinemachine confiner——设置collider2d物体为边界bounding shape 2d
创建空物体——添加碰撞器collider——(碰撞器有多种类型)(需要使用polygon collider 2d多边形碰撞器)
调整碰撞器的形状,小于整体的地形
层级 控制碰撞检测(指物体右上角的layer;不是renderer里面的order in layer)
添加层级:add ——(Confiner——放collider2d物体)
设置层级不发生碰撞检测:edit——project settings——physics2d——层级碰撞矩阵——按需要取消勾选(全部取消 Confiner不与任何层级发生碰撞)
ruby移动的边界限制:四周设置空气墙
创建空物体——添加碰撞器collider2d——调整大小,放在地形的四边
地图完善:其他物体
同类物体放在空的父对象下 ,方便管理
同一对象设置为预制体,方便重复使用
房子、荷花、电线杆、小草、大树……
房子、电线杆、大树:
设置锚点:图片文件——edit——移动蓝点的位置
物体——sprite renderer——sprite sort point——pivot轴心点
添加collider2d组件——edit——调整碰撞器的大小
设为预制体
四周遮挡的树:
图片文件tree foreground(灰色)
物体:调整颜色sprite renderer——color
覆盖关系:按照情况调整order in layer
粒子系统
特效精灵图集:sprite mode——multiple——edit切割图片
创建粒子系统:右键effects——particle System
根据需要勾选组件
常用选项
- constant固定
- random between two constants 设置两个参数(精灵数组的索引参数) 不包含后一个数字
【机器人故障-烟雾特效】
z轴为0
particles Systems基础属性
start lifetime:粒子的存在时间——random between two constants(1.5)(3)
start speed:粒子速度——random between two constants(0.5)(1)
start size:粒子大小——random between two constants(0.3)(0.5)
simulation space:仿真位置(烟雾不跟随:world)
texture sheet Animation贴图纹理
mode:sprites 设置图片资源 加号添加更多图片资源
start frame:开始帧——random between two constants [0,2)
frame over time:随着时间,粒子图片发生变化
点击曲线——右下角展开详情——remove删除曲线(需要保存)
shape特效形状
angle:角度(5)
radus:半径(0)
color over lifetime颜色变化
渐变编辑gradient editor:上方透明度,下方颜色(最右上Alpha=0)
size over lifetime大小变化
点击曲线——右下角展开详情——编辑曲线(最高到最下\)
【修复后机器人停止烟雾特效】
》EnemyController
public ParticleSystem smokeEffect;//public需要在面板赋值;不必须getcomponent
public void Fix()//修复机器人
{
//……
smokeEffect.Stop();//效果逐渐消失
//Destroy(smokeEffect);效果会突然消失
}
在面板赋值smokeEffect
【击打特效-爆炸效果】
rotation:-900
particles Systems基础属性
looping:循环播放(取消勾选)
start lifetime:粒子的存在时间——random between two constants(0.1)(0.2)
start speed:粒子速度(0)
start size:粒子大小——random between two constants(0.7)(1)
start rotation:粒子开始的旋转角度——random between two constants(0)(360)
emission发射器
rate over time:随时间改变(0)
加号burst:爆发(count 4)
shape特效形状
shape(circle圆形)
radus:半径(0.12)
arc–mode(burst spread爆炸)
color over lifetime颜色变化
渐变编辑gradient editor(最右上Alpha=0)
size over lifetime大小变化
点击曲线——右下角展开详情——编辑曲线(0.6—1)
texture sheet Animation贴图纹理
mode:sprites 设置图片资源
【道具-拾取特效】
rotationx=0
particles Systems基础属性
duration:持续时间(3)
looping:循环播放(取消勾选)
start lifetime:粒子的存在时间——random between two constants(0.5)(1)
start speed:粒子速度——random between two constants(0.6)(1.5)
start rotation:粒子开始的旋转角度——random between two constants(0)(360)
emission发射器(发射个数)
rate over time:随时间改变(0)
加号burst:爆发(count 20)
shape特效形状
shape(circle圆形)
radus:半径(0.4)
velocity over time粒子本身的速度
speed moditer(curve曲线:0.9—0.4)
color over lifetime颜色变化
渐变编辑gradient editor(最右上Alpha=0)
size over lifetime大小变化
从上面copy曲线paste
rotation over lifetime旋转角度变化
angular velocity(90)
texture sheet Animation贴图纹理
mode:sprites 设置图片资源 加号添加更多图片资源(按照顺序播放)
UGUI
UI
- UGUI
- NGUI
- FairyUI
canvas画布
-
rect Transform组件
-
canvas组件
- render mode渲染模式
- overlay一直在最上方
- camera随着摄像机移动,一直在最上面
- worldspace(例:敌人血条 随敌人移动)
- render mode渲染模式
-
canvas scaler组件 按屏幕大小缩放(自适应)
- UI scale mode缩放模式
- constant固定大小不变
- screen size适应屏幕
- UI scale mode缩放模式
-
graphic raycaster射线检测
游戏物体以unity单位?UI以像素为单位?
头像
canvas
- image血槽——设置图片——set native size——shift等比例缩放——锚点左上角(九宫格第一个)——脚本(UIHealthBar)
- image遮罩——调整大小,遮盖血条——添加mask组件——不显示图片(取消勾选)
- image血条——设置图片——set native size——shift等比例缩放——锚点左上角(九宫格第一个)
- image头像——设置图片——set native size——shift等比例缩放——四个白三角形设置框住ruby头像内容
- image遮罩——调整大小,遮盖血条——添加mask组件——不显示图片(取消勾选)
子对象随着父对象的拉伸缩放:设置锚点
》UIHealthBar
using UnityEngine.UI;
//...
public Image mask;//需要引用命名空间
float originalSize;
public static UIHealthBar instance{ get; private set; }//通过单例模式 进行方法的外部调用
void Awake()
{
instance=this;//this指当前UIHealthBar
}
void Start()
{
originalSize=mask.rectTransform.rect.width;//初始的尺寸
}
public void SetValue(float fillPercent)//设置当前血条的显示值
{
mask.rectTransform.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal,originalSize*fillPercent);//填充轴向。填充的值(初始值*百分比)
}
面板赋值mask
通过单例模式 进行方法的外部调用(单例最好只有一个)(另一种方式:获取getcomponent)
》RubyController
public void ChangeHealth(int amount)
{
//...
UIHealthBar.instance.SetValue(currentHealth/(float)maxHealth);//转类型 获得float类型
}
射线检测
NPC青蛙(animator组件)(collider2d组件)【预制体】
方法1:NPC图片(添加animator组件)
方法2:多选多张图片 拖入hierarchy窗口 创建动画(动画控制器controller+默认动画clip)
层级控制(物体右上角的layer)——添加层级:add ——(NPC——放NPC青蛙)
添加碰撞器——edit控制大小
调整动画时间
检测方式:
- 碰撞检测collider
- 触发检测trigger
- 射线检测Physics2D.Raycast
》RubyController
void Update()
{
//...
if(Input.GetKeyDown(KeyCode.T))//玩家按下键盘t键
{
//射线检测方法
RaycastHit2D hit=Physics2D.Raycast(rigidbody2d.position+Vector2.up*0.2f,
lookDirection,1.5f,LayerMask.GetMask("NPC"));//以ruby为中心发射射线 (起点=锚点在ruby脚上+向上的单位向量,朝向,距离,层级)
if(hit.collider!=null)//检测到了
{
Debug.Log("射线检测到:"+hit.collider);
}
}
}
对话框
创建canvas——render mode:worldspace——修改大小 scale=0.01 width=300 height=200——修改位置 在NPC上面——显示在上面order in layer=10
- 背景image——选择图片——填充:锚点 alt选择右下角
- 文字text——填充:锚点 中间——对齐:居中——字体大小30——字体颜色——粗体bold——边框:outline组件
》NPCDialog(挂载在NPC上)
赋值dialogBox为对话框canvas
public GameObject dialogBox;
//自动关闭功能
public float diaplayTime=4.0f;//间隔时间
private float timerDisplay;//计时器
void Start()
{
dialogBox.SetActive(false);//游戏开始时,隐藏对话框
timerDisplay=-1;
}
void Update()
{
if(timerDisplay>=0)
{
timerDisplay-=Time.deltaTime;//计时器减小
if(timerDisplay<0)
{
dialogBox.SetActive(false);//自动关闭
}
}
}
public void DisplayDialog()//显示对话框
{
timerDisplay=diaplayTime;
dialogBox.SetActive(true);
}
》RubyController
void Update()
{
//...
if(Input.GetKeyDown(KeyCode.T))
{
//...
if(hit.collider!=null)
{
NPCDialog npcDialog=hit.collider.GetComponent<NPCDialog>();//获取组件
if(npcDialog!=null)//非空
{
npcDialog.DisplayDialog();
}
}
}
}
游戏音效、音乐
audioclip类型资源文件
audiolistener创建相机默认挂载的组件(一个场景只能有一个)(接收)
audio source组件(播放)
创建空物体,挂载组件
audioclip属性:赋值文件
play on awake游戏运行后就播放
loop循环播放
volume音量
spatial blend 0-2d 1-3d(音源与摄像机的距离)
ruby的音效
添加audio source组件——play on awake取消
》RubyController
private AudioSource audioSource;
void Start()
{
audioSource=GetComponent<AudioSource>();//获取组件
}
public void PlaySound(AudioClip audioClip)
{
audioSource.PlayOneShot(audioClip);//播放一次
}
拾取道具
》HealthCollectible草莓
赋值audioClip为Collectable
public AudioClip audioClip;
private void OnTriggerEnter2D(Collider2D collision)
{
//...
rubyContoller.ChangeHealth(1);
rubyContoller.PlaySound(audioClip);
Destroy(gameObject);
}
机器人走路
添加audio source组件——赋值audioclip——loop勾选——volume=0.1
ruby受伤
》RubyController
赋值playerHit
public AudioClip playerHit;
public void ChangeHealth(int amount)
{
//...
PlaySound(playerHit);
}
ruby发射子弹
》RubyController
赋值attackSoundClip
public AudioClip attackSoundClip;
public void Launch()
{
//...
PlaySound(attackSoundClip);
}
机器人被修好
》EnemyController
赋值fixedSound
private AudioSource audioSource;
public AudioClip fixedSound;
void Start()
{
//...
audioSource=GetComponent<AudioSource>();//获取组件
}
public void Fix()
{
//...
audioSource.PlayOneShot(fixedSound);//播放一次
}
随机播放 机器人被击中
》EnemyController
赋值hitSounds数组 拖入两个文件
public AudioClip[] hitSounds;
public void Fix()
{
//...
int randomNum=Random.Range(0,2);//范围内的随机整数int 不包含右边的数字
//拓展:随机数方法的重载float randomNum2=Random.Range(1f,2f);浮点数类型 包含两边的数字
audioSource.Stop();//停止播放
audioSource.volume=0.5f;//调整音量大小
audioSource.PlayOneShot(hitSounds[randomNum]);//被击中
Invoke("PlayFixedSound",1f);//延迟调用(方法,延迟的时间)
}
private void PlayFixedSound()
{
audioSource.PlayOneShot(fixedSound);//修好
//Invoke("StopAudioSourcePlay",1f);//延迟调用
}
//private void StopAudioSourcePlay()
//{
// audioSource.Stop();//停止播放
//}
ruby走路
》RubyController
赋值walkSound
public AudioClip walkSound;
void Update()
{
//...
if(!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0))
{
//...
if(!audioSource.isPlaying)//如果没有在播放
{
audioSource.Play();
audioSource.clip=walkSound;
}
}
else//ruby不动
{
audioSource.Stop();//停止播放
}
}
【更正】ruby使用2个audio source组件:分别播放 走路、射击音效
》RubyController
ruby添加2个audio source组件
赋值audioSource、walkAudioSource
public AudioSource audioSource;
public AudioSource walkAudioSource;
void Start()
{
///audioSource=GetComponent<AudioSource>();//获取组件
}
void Update()
{
//...
if(!Mathf.Approximately(move.x,0)||!Mathf.Approximately(move.y,0))
{
//...
if(!walkAudioSource.isPlaying)//如果没有在播放
{
walkAudioSource.Play();
walkAudioSource.clip=walkSound;
}
}
else//ruby不动
{
walkAudioSource.Stop();//停止播放
}
}
public void PlaySound(AudioClip audioClip)
{
audioSource.PlayOneShot(audioClip);//播放一次
}
游戏的完善
机器人击中特效
》EnemyController
赋值hitEffectParticle
设置为预制体
public GameObject hitEffectParticle;
public void Fix()
{
Instantiate(hitEffectParticle,transform.position,Quanternion.identity);//克隆(对象,位置,旋转角度)
}
ruby射击位置的调整
》RubyController
private void Launch()
{
GameObject projectileObject=Instantiate(projectilePrefab,rigidbody2d.position+Vectoe2.up*0.5f,Quaternion.identity);
//...
}
草莓特效
》HealthCollectible
赋值effectParticle
设置为预制体
public GameObject effectParticle;
private void OnTriggerEnter2D(Collider2D collision)
{
//...
Instantiate(effectParticle,transform.position,Quanternion.identity);//克隆(对象,位置,旋转角度)
}
ruby受伤的动画
》RubyController
public void ChangeHealth(int amount)
{
if(amount<0)
{
//...
animattor.SetTrigger("Hit");
}
(解决刺在上面》order in layer=-1)
【任务】
对话框–文字自适应:勾选best fit
接受任务状态:利用之前的单例 (接到任务后 能够发射子弹)
》UIHealthBar
public bool hasTask;//默认为false
》NPCDialog
public void DisplayDialog()
{
//...
UIHealthBar.instance.hasTask=true;
}
》RubyController
private void Launch()
{
if(!UIHealthBar.instance.hasTask)
{
return;
}
//...
}
提交任务
》UIHealthBar
public bool ifCompleteTask;//fixedNum 之后弃用ifCompleteTask
》NPCDialog
赋值dialogText
using UnityEngine.UI;
//...
public Text dialogText;//需要引入命名空间
public void DisplayDialog()
{
//...
if(UIHealthBar.instance.ifCompleteTask)//fixedNum 之后弃用ifCompleteTask
{
dialogText.text="谢谢您,伟大的ruby!";
}
}
任务完成的判定
修复5个以上的机器人
》UIHealthBar
public int fixedNum;//默认为0
》NPCDialog
if(UIHealthBar.instance.fixedNum>=5)
{
dialogText.text="谢谢您,伟大的ruby!";
}
》EnemyController
public void Fix()
{
UIHealthBar.instance.fixedNum++;
}
任务完成的音效
NPC添加audio source组件
》NPCDialog
赋值audiosSource
赋值completeTaskclip
public AudioSource audiosSource;
public AudioClip completeTaskclip;
public void DisplayDialog()
{
//...
audioSource.PlayOneShot(completeTaskclip);
}
任务完成音频 只播放一次
private bool hasPlayed;
public void DisplayDialog()
{
//...
if(!hasPlayed)
{
audioSource.PlayOneShot(completeTaskclip);
hasPlayed=true;
}
}
复活
》RubyController
private Vector3 respawnPosition;
void Start()
{
respawnPosition=transform.position();
}
public void ChangeHealth(int amount)
{
//...
if(currentHealth<=0)
{
Respawn();
}
}
private void Respawn()
{
ChangeHealth(maxHealth);
transform.position=respawnPosition;
}
草莓动画
改变大小add property选择scale
第一帧scale xyz(0.6,0.4,0.4)
中间帧scale xyz(0.4,0.6,0.4)
最后一帧scale xyz(0.6,0.4,0.4)
打包
edit——project settings
company name公司名
product name游戏名称
default icon默认游戏图标
default cursor默认鼠标的图标样式
resolution and presentation——mode显示模式——全屏;窗口(分辨率)
file——build settings
选择场景 add open scenes
选择平台 window
build打包