unity2D学习(12)角色发射子弹

1 思路

效果的想法:按下“Fire1”就可以切换为shoot射击状态,角色就会自动连续发射子弹,在shoot模式下再按下“Fire1”就可以切换回上一个状态。

代码的思路:用一个Empty GameObject作为枪口,然后一个PreFab预制体作为子弹。

2 子弹

新建一个Sprite,并命名为Bullet(子弹)。

然后为Bullet添加Rigidbody 2D(刚体)、Box Collider 2D(碰撞器)、Script(脚本)。其中Rigidbody 2D(刚体)注意Gravity Scale(重力)设置为0,这样可以直线发射子弹,然后勾选掉Freeze Rotation(自由旋转)。Box Collider 2D(碰撞器)里面要勾选is Trigger(扳机),后面代码要用到OnTriggerEnter2D(和OnCollisionEnter2D的区别在上一个文章里面提到过)。

子弹代码:

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

public class BulletController : MonoBehaviour {

    [SerializeField] private float speed = 5f;//子弹的速度
    public Rigidbody2D rig;

    void Start () {
        rig = GetComponent<Rigidbody2D>();//获取子弹刚体组件
        rig.velocity = transform.right * speed;//移动
        Destroy(gameObject, 2);//2秒后销毁子弹,不然子弹会无限多
    }	

    private void OnTriggerEnter2D(Collider2D collision)//触碰到别的碰撞器的时候
    {
        if (collision.gameObject.tag == "Enemy")//如果碰撞对象是敌人
        {
            collision.gameObject.GetComponent<CrabController>().Hurt();//调用敌人的受伤函数,新加入到敌人的里面函数用来扣敌人血量,方便查看效果,不然太快了
        }
        Destroy(gameObject);//只要碰撞到碰撞体就摧毁子弹本身
    }
}

上面的敌人新加的受伤函数会在下面Instantiate函数介绍给出。

3 Prefab介绍

Prefab(预制件):一种资源类型,用来存储在项目视图中反复使用的对象。

如何生成Prefab(以上面的Bullet子弹为例子):只需要把GameObject拖拽到Assert里对应的文件夹,就会在该文件夹下生成对应GameObject 的Prefab。方便后面枪口发射子弹调用创建实例。

如果Prefab拖拽回SampleScene里面,相当于创建了一个实例。

4 Instantiate函数介绍

Instantiate函数:在unity中进行实例化的函数,返回克隆,可以用于GameObject或Component。

重载(overloaded):

参数介绍:

original复制的现有对象
position复制到那个位置
rotation朝向
parent分配新父对象
instantiateInWorldSpace分配父对象时,传递为true可将新对象直接放置在空间中,传递为false以设置对象相对于其新父对象的位置

使用范例:在CrabController代码里添加敌人新加的受伤函数,给子弹碰撞到敌人调用

    变量
    public GameObject ExplodePrefab;//爆炸效果的预制体
    private float Hp = 100f;//敌人的hp一开始的为100f
    函数
    public void Hurt()
    {
        Hp -= 25f;//每次受伤扣除25f
        if (Hp <= 0f)//当血量低于等于0就会被销毁
        {
            Instantiate(ExplodePrefab, transform.position, transform.rotation);//初始化爆炸效果
            Destroy(gameObject);//销毁
        }
    } 

爆炸效果的预制体在下面给出。

5 敌人HP小于0爆炸效果

新建一个名为Explode(爆炸)的Sprite,然后为它添加Animator(动画)、Script(脚本)。

e

动画用素材Assets->Artwork->Sprites->FX->enemy-death的动画图片组(动画添加就不详细写了,在unity2D学习(5)为角色添加动画里面有),记得保持单位像素统一为16。

代码:

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

public class Explode : MonoBehaviour {
    //就是设置一下销毁时间
	void Start () {
        Destroy(gameObject, 0.2f);//0.2秒后销毁爆炸效果,0.2秒也是我设置的爆炸动画完整一次的时间
    }
}

然后把Explode拖到对应文件夹形成预制体,再把预制体拖拽到CrabConttroller对应的explodePrefabe变量去。

枪口

在Player下面新建一个Empty GameObject并取名为Gun(枪)。

把Player的Shoot动画弄出来,然后把Gun的位置调整到角色枪口的地方,因为在Player地下,所以Gun的位置会跟随Player移动。

然后枪口不会跟随角色翻转而反转,要修改一下之前Player的控制代码里面的Flip函数。把原先的transform.localScale = new Vector2(face, 1)改为transform.Rotate(0f,180f,0f),这样枪口也会跟着反转。

    void Flip()
    {
        face = (face == 1) ? -1 : 1;
        //transform.localScale = new Vector2(face, 1);//原来的反转代码
        transform.Rotate(0f,180f,0f);//这样枪口也会一起转向
    }

枪口代码:

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

public class Weapon : MonoBehaviour {
    
    public GameObject bulletPrefab;
    public float fireRate = 0.5F;//0.5秒实例化一个子弹
    private float nextFire = 0.0F;
    public void Shoot()
    {
        if(Time.time > nextFire)//让子弹发射有间隔
        {
            nextFire = Time.time + fireRate;//Time.time表示从游戏开发到现在的时间,会随着游戏的暂停而停止计算。
            Instantiate(bulletPrefab, transform.position, transform.rotation);
        }
    }
}

7 动画转换

添加了新的参数fire(Bool)来判断是否处于射击状态。

为动画转移添加runShoot和Shoot,站立射击和跑动射击。

动画转移参数:

  • idle->shoot:fire为true
  • shoot->idle:fire为false
  • runShoot->shoot:speed小于0.01f
  • shoot->runShoot:speed大于0.01f
  • run->runShoot:fire为true
  • runShoot->run:fire为false

8 Player控制的总代码

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

public class PlayerController : MonoBehaviour
{
    private Rigidbody2D rig;//刚体
    private Animator Anim;//角色的Animator

    [Header("Layers")]
    public LayerMask groundLayer;//用来开启layer

    [Space]
    [Header("Speed")]
    private float moveSpeed=360f;//移动速度
    private float climbSpeed=100f;//爬行速度

    [Space]
    [Header("Force")]
    private float moveForce=150f;//移动力
    private float jumpForce=575f;//跳跃力

    [Space]
    [Header("Frequency")]
    private int jumpMax=2;//跳跃次数的上线
    private int jumpNum=0;//当前跳跃的次数

    [Space]
    [Header("Booleans")]
    private bool falling = false;//用来标记是否是下落状态
    private bool onWall;//是否在墙上
    private bool onJumping;//是否正在跳跃中
    private bool onGround;//是否正在地上
    private bool onHurt;//是否受伤
    private bool onShooting;//是否开枪
    [Space]
    [Header("Collision")]
    private float collisionRadius = 0.15f;//碰撞半径
    private Vector2 bottomOffset, rightOffset, leftOffset;//下左右相对于角色中心的二维向量
    private Collider2D coll;//角色的碰撞器
    [Space]
    [Header("UI")]
    private Text HpNumberText;
    [Space]
    private float face = 1;//角色朝向,初始朝向向右边
    private float HP=100f;//角色血量
    public GameObject weapon;//调用武器

    //初始化
    void Start()
    {
        rig = GetComponent<Rigidbody2D>();//获取主角刚体组件
        Anim = GetComponent<Animator>();//获取主角动画组件
        coll = GetComponent<Collider2D>();//获取角色碰撞器
        weapon= GameObject.Find("Gun");//获取枪
        HpNumberText = GameObject.Find("HpNumber").GetComponent<Text>();//获取ui对于的text

        groundLayer = 1 << 8;//开启Ground的layer层,Ground在layer8

        onWall = false;//初始不在墙上
        onJumping = false;//初始不正在跳跃
        onGround = true;//初始在地上
        onShooting = false;//初始不在射击
        onHurt = false;//初始不受伤

        bottomOffset = new Vector2(0,-1.6f);
        rightOffset = new Vector2(0.61f,-1.3f);
        leftOffset = new Vector2(-0.72f,-1.3f);  
    }
    //固定的时间间隔执行,不受游戏帧率的影响
    void FixedUpdate()
    {
        Movement();
        changeAnimator();
    }
    //控制移动
    void Movement()
    {
        float moveMultiple = Input.GetAxis("Horizontal");
        float faceDirection = Input.GetAxisRaw("Horizontal");
        float verticalMove = Input.GetAxis("Vertical");
        if (onShooting)//在射击状态
        {
            shoot(moveMultiple, faceDirection);
            isShooting();
        }
        else if (onGround)//在地上
        {
            if (isShooting())
            {
                shoot(moveMultiple, faceDirection);
            }
            move(moveMultiple, faceDirection);//左右移动
            if (touchWall())//是否可爬墙,碰到墙
            {
                if (verticalMove>0)//是否有爬墙的行为,按向上键
                {
                    onGround = false;
                    onWall = true;
                    Climb(verticalMove);
                }
            }
            if (Input.GetButtonDown("Jump"))
            {
                onGround = false;
                onJumping = true;
                Jump();
            }
        }
        else if (onJumping)//在空中跳跃
        {
            if (touchWall())//可以爬墙
            {
                onJumping = false;//关闭现在状态
                onWall = true;//开启下一个状态
                Climb(verticalMove);//爬行
                jumpNum = 0;//跳跃次数清零
            }
            else if (Input.GetButtonDown("Jump"))//二段跳
            {
                Jump();
            }
        }
        else if (onWall)//在墙上
        {
            if(Input.GetButtonDown("Jump"))
            {
                onWall = false;
                onJumping = true;
                Flip();
                Jump();
                
            }
            else if (touchWall())//如果碰到墙,就说明还在爬
            {
                Climb(verticalMove);
            }
            else//如果说爬到了末端,就可以有跳跃行为,不然的话难爬到地面
            {
                onWall = false;
                onJumping = true;
                Jump();
            }
        }
    }
    void Flip()//反转
    {
        face = (face == 1) ? -1 : 1;//在墙上跳出的话,动画要和原来相反
        transform.Rotate(0f,180f,0f);//这样枪口也会一起转向
    }
    bool isGround()//判断是否碰地
    {
        return Physics2D.OverlapCircle((Vector2)transform.position + bottomOffset, collisionRadius, groundLayer);//判断是否碰到地面
    }
    bool isShooting()//判断是否射击
    {
        if (Input.GetButtonDown("Fire1"))
        {
            onShooting=(onShooting == true)? false : true ;
            Debug.Log(onShooting);
        }
        return onShooting;
    }
    void shoot(float moveMultiple, float faceDirection)
    {
        weapon.GetComponent<Weapon>().Shoot();
        move(moveMultiple, faceDirection);//左右移动
    }
    bool touchWall()//判断是否碰到墙
    {
        return Physics2D.OverlapCircle((Vector2)transform.position + rightOffset, collisionRadius, groundLayer)
            || Physics2D.OverlapCircle((Vector2)transform.position + leftOffset, collisionRadius, groundLayer); 
    }
    void move(float moveMultiple,float faceDirection)//移动代码
    {
        //角色左右移动
        if (moveMultiple != 0)
        {
            //velocity表示速度,Vector表示向量
            rig.velocity = new Vector2(moveMultiple * moveSpeed * Time.deltaTime, rig.velocity.y);//输入x,y向量,数值*方向
        }
        //角色朝向修改
        if (faceDirection != 0&&face!=faceDirection)
        {
            Flip();
        }
    }
    void Jump()//跳跃代码
    {
        if(jumpNum < jumpMax)
        {
            rig.velocity = new Vector2(face*moveForce*Time.deltaTime, jumpForce * Time.deltaTime);
            jumpNum++;
        }   
    }
    void Hurt(Collision2D collision)//受伤代码
    {
        onHurt = true;
        AccordingDirectionFlip(collision);
        rig.velocity = new Vector2(face * moveForce * Time.deltaTime, jumpForce * Time.deltaTime);
    }
    void AccordingDirectionFlip(Collision2D collision)//根据敌人方向,安排玩家转向
    {
        if (collision != null)//如果玩家出现视野中
        {
            int direction;
            if (collision.transform.position.x < transform.position.x)
            {
                direction = -1;//玩家在敌人的左边
            }
            else
            {
                direction = 1;//玩家在敌人的右边
            }
            if (direction != face)//表示方向不一致
            {
                //Debug.Log(direction);
                Flip();
            }
        }
    }
    void Climb(float verticalMove)//爬墙代码
    {
        rig.velocity = new Vector2(rig.velocity.x,climbSpeed * verticalMove* Time.deltaTime);//输入x,y向量,数值*方向
    }
    void changeAnimator()//动画切换
    {
        if (onGround)//如果在地上
        {
            Anim.SetFloat("speed", Mathf.Abs(rig.velocity.x));//速度是向量
            if (onHurt)
            {
                Anim.SetBool("injured", true);
                onGround = false;
            }
            if (onShooting)
            {
                Anim.SetBool("fire", true);
            }
            else
            {
                Anim.SetBool("fire", false);
            }
        }
        if (onWall)//如果在墙上
        {
            Anim.SetBool("wall", true);
            if (Anim.GetBool("ground") == false)
            {
                Anim.SetBool("ground", true);
            }
        }
        else
        {
            Anim.SetBool("wall", false);
        }
        if (onJumping)//如果在跳跃状态
        {
            if (onHurt)
            {
                Anim.SetBool("injured", true);
                jumpNum = 0;//当前跳跃次数清零
                falling = false;
                onJumping = false;
            }
            else if (Anim.GetBool("ground"))
            {
                falling = false;
                Anim.SetBool("ground", false);
            }
            else 
            {
                if (falling&&isGround())
                {
                    Anim.SetBool("ground", true);
                    jumpNum = 0;//落地的话,当前跳跃次数清零
                    falling = false;
                    onJumping = false;
                    onGround = true;
                }
                else if(rig.velocity.y < 0)
                {
                    falling = true;
                }
            }

        }
        if (onHurt)//如果在受伤状态
        {
            if (falling && isGround())
            {
                Anim.SetBool("injured", false);
                Anim.SetBool("ground", true);
                falling = false;
                onGround = true;
                onHurt = false;
            }
            else if (rig.velocity.y < 0)
            {
                falling = true;
            }
        }
    }
    void OnDrawGizmos()//绘制辅助线
    {
        Gizmos.color = Color.red;//辅助线颜色
        //绘制圆形辅助线
        Gizmos.DrawWireSphere((Vector2)transform.position + bottomOffset, collisionRadius);
        Gizmos.DrawWireSphere((Vector2)transform.position + rightOffset, collisionRadius);
        Gizmos.DrawWireSphere((Vector2)transform.position + leftOffset, collisionRadius);
    }
    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.tag == "Enemy")
        {
            HP -= 25f;//碰到一次敌人减去25血量
            HpNumberText.text = HP.ToString();//显示HP
            Hurt(collision);
        }
    }
}

9 游戏效果

  • 17
    点赞
  • 86
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 您好,我可以回答这个问题。Unity2D角色移动可以通过编写脚本来实现。您可以使用Input.GetAxis函数获取水平和垂直输入,然后使用Transform.Translate函数将角色移动到新的位置。您还可以使用Rigidbody2D组件来实现更流畅的移动。希望这可以帮助您。 ### 回答2: Unity2D角色移动可以通过以下几种方式实现: 1. 使用刚体组件(Rigidbody2D):将刚体组件添加到角色对象中,然后通过代码控制刚体的速度(rigidbody2D.velocity)或者力(rigidbody2D.AddForce)进行角色移动。可以通过键盘输入或者触摸输入来控制角色的移动方向。 2. 使用Transform组件:直接通过修改角色对象的Transform组件中的position属性,来改变角色的位置。可以使用键盘输入或者触摸输入来控制角色的移动方向。 3. 使用Translate方法:在更新角色移动的函数中,使用Translate方法来改变角色的位置,可以通过键盘输入或者触摸输入来控制角色的移动方向。 4. 使用CharacterController组件:将CharacterController组件添加到角色对象中,然后通过代码控制CharacterController的Move函数来改变角色的位置。可以通过键盘输入或者触摸输入来控制角色的移动方向。 需要注意的是,以上方法都需要在Update函数中进行角色移动的操作,并且要根据游戏需求进行相应的优化,例如添加碰撞检测、地图边界限制等。另外,角色移动过程中还可以结合动画、音效等来提高游戏的表现力和用户体验。 ### 回答3: 在Unity 2D中,角色移动可以通过几种不同的方法来实现。其中最常用的方法是使用Rigidbody2D组件来控制角色的移动。 首先,需要在角色对象上添加Rigidbody2D组件。然后,可以通过编写脚本来控制角色的移动。以下是一个简单的示例脚本: ``` using UnityEngine; public class PlayerMovement : MonoBehaviour { public float moveSpeed = 5f; // 角色的移动速度 private Rigidbody2D rb; private void Start() { rb = GetComponent<Rigidbody2D>(); } private void Update() { float moveHorizontal = Input.GetAxis("Horizontal"); // 获取水平方向的输入 float moveVertical = Input.GetAxis("Vertical"); // 获取垂直方向的输入 Vector2 movement = new Vector2(moveHorizontal, moveVertical); // 构建角色移动的向量 rb.velocity = movement * moveSpeed; // 设置角色的速度 } } ``` 在这个示例中,我们首先获取用户的水平和垂直输入,并将其构建为一个向量。然后,通过将该向量与移动速度相乘,我们得到了角色的速度。最后,我们使用Rigidbody2D组件的velocity属性来设置角色的速度。 通过调整moveSpeed变量的值,可以改变角色的移动速度。你也可以根据自己的需求来修改脚本,以满足不同的移动需求,如斜向移动、限制移动范围等。 以上就是使用Rigidbody2D组件来实现Unity 2D角色移动的简单示例。希望对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值