ruby‘s adventure笔记

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(例:敌人血条 随敌人移动)
  • canvas scaler组件 按屏幕大小缩放(自适应)

    • UI scale mode缩放模式
      • constant固定大小不变
      • screen size适应屏幕
  • graphic raycaster射线检测

游戏物体以unity单位?UI以像素为单位?

头像

canvas

  • image血槽——设置图片——set native size——shift等比例缩放——锚点左上角(九宫格第一个)——脚本(UIHealthBar)
    • image遮罩——调整大小,遮盖血条——添加mask组件——不显示图片(取消勾选)
      • image血条——设置图片——set native size——shift等比例缩放——锚点左上角(九宫格第一个)
    • image头像——设置图片——set native size——shift等比例缩放——四个白三角形设置框住ruby头像内容

子对象随着父对象的拉伸缩放:设置锚点

》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打包

  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值