unity2D备忘志(二)

随着程序越来越复杂,一下章节不能再贴全部代码了,只贴涉及到的部分,但全部代码会在文末给出

子弹这一部分涉及多个知识点:简单归纳如下
1.给刚体施加力量让其移动
2.利用public一个对象,然后界面绑定的方法传递预制体
3.预制体实例化

十四. 子弹

1.初始化

首先,找到子弹素材,将其做成一个预制体

给他添加刚体 和 碰撞盒

创建一个脚本bullet来控制子弹

2.子弹移动

这里我们用另一种方法来让刚体移动 ==>施加力量

        public void BulletMove(Vector2 moveDirection,float moveForce)
    {
        bulletRigid.AddForce(moveDirection*moveForce);//给子弹添加一个力,它就会受力移动
    }

      

3.主角脚本绑定子弹预制体

由于主角是发射子弹的主体,所以,发射代码写在主角的脚本中,在主角脚本中如何获得子弹的预制体呢? 我们利用public一个对象,然后界面绑定的方法来传递预制体

首先我们public一个gameobject对象

        public GameObject bulletPreb;

      

然后,在界面上将子弹的预制体绑定过去

v2-caa19a061251dbaf70f4bfcddd4d1469_b.jpg

这种方法简单,高效!接下来就是实例化这个预制体

4.发射子弹

比如:我们按下J键,就发射一个子弹

这里用到了预制体实例化的函数Instantiate(); 实例化后调用子弹移动函数,使其移动

                if (Input.GetKeyDown(KeyCode.J))
        {
            GameObject bullet= Instantiate(bulletPreb,rubyBody.position,Quaternion.identity);
            bullet.GetComponent<bullet>().BulletMove(lookVector,300);
        }

      

完成了!!!!

但是,效果和我们想象的完全不一样....

效果不尽人意,原因有好几个,我们一个一个的解决

问题1:角色不应该和子弹有刚体碰撞,这里就要把角色和子弹分好层了(其实所有节点创建之时起就都应该分好层)

v2-440a91380224f9bf16babd4921017c45_b.png

v2-a77c360bdaf3f519fa109586b161baf5_b.jpg

然后在项目设置中将player和bullet的层间碰撞取消

unity2D备忘志(二)_第1张图片

unity2D备忘志(二)_第2张图片

此外,后来我将bullet之间的碰撞也取消了,这样就能保证某些极端情况下,子弹互相碰撞.

unity2D备忘志(二)_第3张图片

问题2:

效果终于发生了变化,但是子弹怎么不动呢?

而且控制台还报错

v2-b0bcb5727ca7a6dd821248981791472d_b.png

问题出在了,子弹初始化的逻辑上

我们获取子弹刚体,不应该在start里 ,而应该在awake里,

这里说一下start和awake的区别:

1.Awake()是在脚本对象实例化时被调用的,而Start()是在对象的第一帧时被调用的,而且是在Update()之前,先执行Awake方法,再执行Start方法
2.脚本的一些成员,如果想在创建之后的代码中立即使用,则必须写在Awake()里面;
3.当脚本设置为不可用时,Awake方法仍然会执行一次,而Start方法则不会执行!
所以我们可以简单理解为:实例物体创建后就awake,执行脚本开始时start,
awake是实例对象的初始化,start是脚本的初始化

这里我们想实例对象创建后立即执行所以应该将此初始化写在awake里

v2-d8b62bfa399f54eb08f9e1f27138aede_b.jpg 错误的
            private void Awake() 
    {
        bulletRigid=GetComponent<Rigidbody2D>();
    }

      

终于可以了,接下来我们为子弹添加碰撞和自毁程序,在给角色添加射击动画

5. 子弹碰撞和自毁

子弹不能永远的飞行下去,当他碰到别的物体或这飞行一段时间后,应该自毁

首先我们来做飞行一段时间后自毁:

这个很简单

        Destroy(gameObject,2f);  //写在awake里即可  awake两秒后就会自毁

      

再写碰撞自毁:

            private void OnCollisionEnter2D(Collision2D other) 
    {
        Destroy(gameObject);
    }

      

6. 给角色添加发射动画

再添加角色的射击动画:

                if (Input.GetKeyDown(KeyCode.J))
        {
            GameObject bullet= Instantiate(bulletPreb,rubyBody.position,Quaternion.identity);
            bullet.GetComponent<bullet>().BulletMove(lookVector,300);
            anim.SetTrigger("launch");  //触发发射动画
        }

      

其实这里有问题,就是没有限制角色的发射频率,因为此段代码写在update里,所以很好实现.这个功能以后完善.

7. 子弹击中敌人

子弹击中敌人,应该做以下事情,

1.子弹击中消失;

2. 敌人播放被击中的动画 //这里注意,敌人被击中时不要再继续执行移动等动作.

3.敌人播放完动画后消失

我们在敌人脚本中建立了一个布尔变量记录角色被击中的状态

        private bool fixedState;//角色被击中否,为被击中:false 击中了:true

      

同时在start里初始化为false

        fixedState=false;

      

再給敌人增加一个函数,当被击中时触发

            public void BeFixed()
    {
        anim.SetTrigger("fixed");    //播放击中动画
        fixedState=true;             //将敌人的状态修改为被击中
        Destroy(gameObject,1f);      //1秒后自爆
    }

      

接下来我们要做的,就是再子弹击中他时触发事件了~

在子弹脚本中:

            private void OnCollisionEnter2D(Collision2D other) {
        Destroy(gameObject); 
        if(other.gameObject.tag=="Enemy"){                     //判断一下打中的是不是敌人
            other.gameObject.GetComponent<enemy1>().BeFixed(); //敌人被击中调用BeFixed函数
        }
    }

      

十五. 敌人特效

在场景中创建一个特效

unity2D备忘志(二)_第4张图片

我们在其属性框中,将其纹理选项打开

unity2D备忘志(二)_第5张图片

选中精灵模式

unity2D备忘志(二)_第6张图片

将下面的特效图片框增加到2

v2-ed95cb1cd804e7a01f7aeb87e0860a6d_b.png

把我们提前准备好的精灵图片传递进去

v2-610afb5ba1e23e8edbffcf1cd3d6f12c_b.png

将开始帧设为0~2随机,这样久能随机上面的两张特效图片了(点后面的小倒三角)

unity2D备忘志(二)_第7张图片

在render选项卡中,选好图层

unity2D备忘志(二)_第8张图片

然后设置特效细节,好多特效参数都能随机,增加趣味性

unity2D备忘志(二)_第9张图片

把模拟空间设为world,可以使得特效挂载在物体上随物体移动时,有火车拉烟的那种效果

unity2D备忘志(二)_第10张图片

设定特效消失的时间点

v2-ab3af803198a57a47405ba88afbb57e2_b.png

在shape选项卡中,可以设计特效的形状样式

unity2D备忘志(二)_第11张图片

在color over lifeTime选项卡中,可以设计特效随进程的颜色及透明度变换

v2-ce56a412b0d672f37ac0622313602ffd_b.png

如下图:上面的游标是透明度,下面的游标是颜色(设置起来非常简单)

unity2D备忘志(二)_第12张图片

特效调整完毕后,我们将它制作为预制件,将预制件直接挂在想添加特效的节点下即可

v2-306c7d3a408a9e39f6544d16c238af0a_b.jpg

特效挂在节点下后,记得调节好特效在父节点坐标系中的位置

这样一只浓烟滚滚的小机器人就做好了

特效在脚本中的管理

比如我们要做机器人被击中后,停止冒烟(因为角色发射的是齿轮,射击是为了帮它修复....)

这里我们用属性面板绑定的方法传递特效

先public一个特效

        public ParticleSystem effectSmoke;   //机器人冒烟特效

      

再在属性面板中绑定该特效

unity2D备忘志(二)_第13张图片

然后,我们就可以操作和管理此特效了

            public void BeFixed()
    {
        anim.SetTrigger("fixed");
        fixedState=true;
        Destroy(gameObject,2f);
        if(effectSmoke.isPlaying) effectSmoke.Stop();
    }

      

十六 吃草莓特效

还是同样的配方,创建特效调参数

添加三张素材

unity2D备忘志(二)_第14张图片

这里我们调节形状时选择了圆形

unity2D备忘志(二)_第15张图片

调节基本特效时,把booping关掉,只播放一次

v2-60a8fbcccaf8b41125f338bea449095b_b.jpg

选择爆发选项卡,添加一种爆发

v2-fd8693f5b22a6ef55b55168e4446f22f_b.jpg

选择 size over LifeTime 控制动画中的元素大小曲线

unity2D备忘志(二)_第16张图片

最后将调到满意的效果做成预制件

再脚本中,我们public一个特效接口,

        public ParticleSystem strawEffect;

      

在属性面板中将特效预制件传递给它

最后我们想触发时,直接实例化一个特效就可以了

            private void OnTriggerEnter2D(Collider2D other) {
        if (other.tag=="Player")
        {
            if (other.GetComponent<MyRuby>().getHp()<other.GetComponent<MyRuby>().getMaxHp())
            {
                other.GetComponent<MyRuby>().changeHp(1);
                Instantiate(strawEffect,transform.position,Quaternion.identity);  //角色吃草莓时实例化一个特效预制件
                Destroy(this.gameObject);
            }
        }
    }

      

最终结果,闪闪惹人爱!~

十七. 血条

新建一个UI image

unity2D备忘志(二)_第17张图片

这时,场景会自动建立下面的节点结构

v2-7578b3a44e186cdcbe6dd6afe188d202_b.png

这时我们先要调整血条UI适应屏幕尺寸 在canvas属性里调节

v2-a1eb57a2eec1935931e41c5af652166b_b.jpg

将准备好的素材图给image,并调整好位置

unity2D备忘志(二)_第18张图片

再建一个image 绑定头像 再建一个image充当血条,整体效果如下.

unity2D备忘志(二)_第19张图片

其中血条因为要变化,所以将其图片类型改为填充型,并将填充方式改为水平

v2-c69a96b57be574bdabe855fcb41b1be8_b.jpg

最终结构是这样的

v2-59e6bbf251f1ae3e37cfec57e484eae6_b.jpg

接下来,我们写代码:

我们建立一个脚本专门管理UI 名为uicontroller,将它直接挂在canvas上

首先,我们要获取UI相关的数据类型,需要引用UI相关的命名空间

        using UnityEngine.UI;

      

其次,为了方便多个脚本里调用UI中的函数,我们直接单例一个UI

        public static UIcontroller instance{get;private set;}
void Start()
    {
        instance=this;
    }

      

然后我们在脚本里写好血条更新的方法

        using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UIcontroller : MonoBehaviour
{
    public Image hpBar;   //注意:hpbar采用的绑定法传递
    public static UIcontroller instance{get;private set;}
    // Start is called before the first frame update
    void Start()
    {
        instance=this;
    }
    public void HpShow(int nowHp,int maxHp){
        Debug.Log(nowHp+"?"+maxHp);
        hpBar.fillAmount=(float) nowHp / maxHp;
    }
}

      

然后再主角的脚本中调用这个方法,主角初始化及血量变化时都需要调用此方法

            public void changeHp(int change){
        RubyHp=Mathf.Clamp(RubyHp+change,RubyMinHp,RubyMaxHp);
        UIcontroller.instance.HpShow(RubyHp,RubyMaxHp);
    }

      

这样血条就制作完成了!

十八 添加音效

  1. 添加背景音乐

建立一个空物体,添加audio source组件,为其audioclip传递事先准备好的bgm

unity2D备忘志(二)_第20张图片

2.添加吃草莓的音效

创建一个管理音效的脚本,里面写好音乐播放的方法,将此脚本单例,方便各处调用

        using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class audioManager : MonoBehaviour
{
    public static audioManager instance{get;private set;}
    // Start is called before the first frame update
    void Start()
    {
        instance=this;
    }
    public void AudioPlay(AudioClip clip){
        GetComponent<AudioSource>().PlayOneShot(clip);
    }
}

      

实例化之后,就可以给大家使用了,

在草莓脚本中,添加一个传递音频的接口,并把对应的素材传递给它

        public AudioClip eatAudioClip;

      

在碰撞触发时调用音频播放,并给它传递素材音频对象

         audioManager.instance.AudioPlay(eatAudioClip);

      

3.给角色添加受伤和射击音效,子弹击中音效,原理同上

4.机器人行走音效

给机器人直接加一个Audio source,并给它素材,同时,为了增加立体声效果,应将混合特效调为3D,同时调节播音范围

unity2D备忘志(二)_第21张图片

unity2D备忘志(二)_第22张图片

我们可以在3D声音设置里调整具体效果.

unity2D备忘志(二)_第23张图片

此外,为了增加沉浸感,给主角加一个audio listener 来收声音,把camera的audio listener禁用掉.

5. 主角行走音效

主角受玩家控制,有移动和静止两个状态,所以要做一下判断.此外这段音频较长(相较于update),所以应该判断一下是否在播放,此外停止移动应该立即停止音效.

其他和敌人移动基本一致

            private AudioSource walkAudioSource;
    private void Start() {
        walkAudioSource=GetComponent<AudioSource>();
    }
    void Update()
    {
         if(moveVector.magnitude!=0){
            lookVector=moveVector;
            anim.SetFloat("lookX",lookVector.x);
            anim.SetFloat("lookY",lookVector.y);
            if(!walkAudioSource.isPlaying){
                walkAudioSource.Play();
            }
        }
        else
        {
            walkAudioSource.Stop();
        }
    }

      

十九 NPC交互

1.创建NPC

首先我们制作一个NPC,这和制作敌人的方法是一样的

然后为其添加碰撞盒.

2.添加主角射线

我们跟NPC交互的思路是:主角发射一束隐藏的射线,碰到NPC时,触发对话.

这里涉及到给主角添加射线束的知识

我们创建一个射线,

        private RaycastHit2D rubyRay;

      

再在update里设定射线的属性,一直发射射线,如果碰到NPC就会显示对话,我们先测试一下

        rubyRay=Physics2D.Raycast(rubyBody.position,lookVector,1f,LayerMask.GetMask("NPC"));
if (rubyRay.collider!=null)
        {
            Debug.Log("NPC");
        }

      

3. 添加NPC对话框

给NPC加一个UI canvas,这个canvas出现在场景中,所以我们将它的渲染模式改为world space,并设置好大小

unity2D备忘志(二)_第24张图片

然后我们给canvas加一个背景图片 ui image, 图片采用九宫格模式,再设置一下拉伸模式,按住alt,选择将图片铺满画布(由于热键冲突,下面的截图没有按alt)

unity2D备忘志(二)_第25张图片

在给canvas添加一个显示文本的组件 为了显示效果好,给text组件添加了一个outline组件来描边

最终结构

v2-431464c0625048a5edf5356b1bc2c49d_b.jpg

按照上述方法,建立了两个对话框,提示对话框和触发对话框,平时显示提示对话框,触发后显示触发对话框.

unity2D备忘志(二)_第26张图片

给NPC创建一个脚本,里面写好对话框触发方法

        using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NPC : MonoBehaviour
{
    public void ShowDialogue(){
        transform.Find("showCanvas").gameObject.SetActive(true);
        transform.Find("hideCanvas").gameObject.SetActive(false);
        Invoke("hideDialogue",4f);                      //对话框会在4秒后自动消失
    }
    public void hideDialogue(){
        transform.Find("showCanvas").gameObject.SetActive(false);
        transform.Find("hideCanvas").gameObject.SetActive(true);
    }
}

      

最后在主角脚本中触发这些方法即可

为了防止主角一直发射线消耗性能,改为了用快捷键触发的方式

                if (Input.GetKeyDown(KeyCode.E))
        {
            if (rubyRay.collider!=null)
                {
                    rubyRay.collider.gameObject.GetComponent<NPC>().ShowDialogue();
                }
        }

      

二十 子弹数量及UI

用的都是之前的知识和方法,可以毫无难度的自己做出来,

甚至我是直接复制了草莓的预制件,进行了魔改 公用了collectThings脚本和strawberryEffect

以下是部分代码:

主角脚本:

定义了 当前子弹和总子弹变量

            private int currentBullet=10;
    private int maxBullet=99;

      

增加了changeBullet方法

            public void changeBullet(int bullet)
    {
        currentBullet =Mathf.Clamp(currentBullet+bullet,0,maxBullet);
        UIcontroller.instance.SetBulletTxt(currentBullet,maxBullet);
    }

      

武器攻击时做了判断

                if (Input.GetKeyDown(KeyCode.J))
        {
            if(currentBullet>0)
            {
                GameObject bullet= Instantiate(bulletPreb,rubyBody.position+new Vector2(0,0.5f),Quaternion.identity);
                bullet.GetComponent<bullet>().BulletMove(lookVector,300);
                anim.SetTrigger("launch");
                audioManager.instance.AudioPlay(lunachAudioClip);
                changeBullet(-1);
            }
            else
            {
                Debug.Log("Out of Bullet!!!");
            }
        }

      

collectThings脚本:

草莓和子弹公用collectThings,代码简单将草莓和子弹区分开.

        using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class collectThings : MonoBehaviour
{
    // Start is called before the first frame update
    public ParticleSystem strawEffect;
    public AudioClip eatAudioClip;
    private void OnTriggerEnter2D(Collider2D other) {
        if (other.tag=="Player")
        {
            if (other.GetComponent().getHp()().getMaxHp())
            {
                if(gameObject.name=="CollectibleBullet")
                {
                    other.GetComponent().changeBullet(10);
                }
                else
                {
                    other.GetComponent().changeHp(1);
                }
                
                Instantiate(strawEffect,transform.position,Quaternion.identity);
                audioManager.instance.AudioPlay(eatAudioClip);
                Destroy(this.gameObject);
            }
        }
    }
}
      

UIcontroller脚本增加更新子弹显示的方法

            public void SetBulletTxt(int currentBullet,int maxBullet){
        if(currentBullet<=maxBullet & currentBullet>=0){
            bulletTxt.text=currentBullet+" / "+maxBullet;
        }        
    }

      

你可能感兴趣的:(unity2D备忘志(二))