【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

这篇具有很好参考价值的文章主要介绍了【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

开头资源地址:B站搬运:【Unity教程】如何在Unity当中实现像《Celeste(蔚蓝)》中完美的跳跃手感【转载搬运】【自翻】_哔哩哔哩_bilibilihttps://youtu.be/STyY26a_dPY建议配合卡姐翻译的这个视频食用更佳https://www.bilibili.com/video/BV1M441197sr?share_source=copy_web在Patreon上支持作者的链接https://www.patreon.com/mixandjam项目工程Github链接-----------------------------https://www.bilibili.com/video/BV1xr4y1s71V?spm_id_from=333.851.header_right.history_list.click

Github:完整项目:

GitHub - mixandjam/Celeste-Movement: Recreating the movement and feel from Celestehttps://github.com/mixandjam/Celeste-Movement

学习目标:

  作为IGN上少数满分10分的游戏,蔚蓝的操作手感始终是被无数游戏制造者所称赞的,偶然间看到了有个很优秀的Untiy项目制作者介绍了如何还原蔚蓝的手感今天就来学习下怎么制作如蔚蓝般极佳的操作手感,虽然做不到百分百还原,但如果有半成以上的实现也算颇有收获。


学习内容:

首先我们要先搭建好游戏的场景,利用TileMap将作者已经切割好的Sprite放到我们的Tile Pattle面板中,这里我创建了三个Grid,并把绘制地板的那个Grid添加好Tilemap Collider 2D以及Composite和Rigibody2D,需要提醒的一点是,别忘了设置一下这三个Grid的Sorting Layer,无论如何人物的层肯定要显示在最上面。【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

然后就是设置人物。先创建一个空对象。并给它相应的组件,【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

正如我前面讲的,将人物的显示部分作为子对象然后再给它Sprite Render,并把我们人物精灵图给它【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

接下来调整它的Sorting Layer以及创建好动画给它【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

动画的有限状态机我们先放一边,先来制作人物的碰撞检测的脚本

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

public class PlayerCollision : MonoBehaviour
{

    [Header("地面层级")]
    public LayerMask groundLayer;

    [Space]
    public bool onGround;
    public bool onWall;
    public bool onLeftWall;
    public bool onRightWall;
    public int wallSide;

    [Header("检测参数")]
    public float collisionRadius = 0.25f;
    public Vector2 bottomPos, leftPos, rightPos;
    private Color collisionColor = Color.red;
   
    void Update()
    {
        // Physics2D.OverlapCircle形成圆形检测通过我们设置的对应检测参数来设置
        onGround = Physics2D.OverlapCircle((Vector2)transform.position + bottomPos, collisionRadius, groundLayer);
        onWall = Physics2D.OverlapCircle((Vector2)transform.position + leftPos, collisionRadius, groundLayer) ||
            Physics2D.OverlapCircle((Vector2)transform.position + rightPos, collisionRadius, groundLayer);
        onLeftWall = Physics2D.OverlapCircle((Vector2)transform.position + leftPos, collisionRadius, groundLayer);
        onRightWall = Physics2D.OverlapCircle((Vector2)transform.position + rightPos, collisionRadius, groundLayer);

        wallSide = onRightWall ? -1 : 1;
    }

    private void OnDrawGizmos()
    {
        //将检测圆形直接在Scene界面上更直观的显示
        Gizmos.color = Color.red;

        var checkositions = new Vector2[] { bottomPos, leftPos, rightPos };
        Gizmos.DrawWireSphere((Vector2)transform.position + bottomPos, collisionRadius);
        Gizmos.DrawWireSphere((Vector2)transform.position + leftPos, collisionRadius);
        Gizmos.DrawWireSphere((Vector2)transform.position + rightPos, collisionRadius);
    }
}

然后再创建一个PlayerMovement,把对应的检测补上,一会动画的时候还要用

public class PlayerMovement : MonoBehaviour
{
    [Space]
    [Header("运动参数")]
    [SerializeField] float moveSpeed;
    [SerializeField] float jumpForece;
    [SerializeField] float dashSpeed;
    [SerializeField] float slideSpeed;
    [SerializeField] float wallJumpLefp;

    private PlayerAnimation anim;
    private PlayerCollision coll;
    [HideInInspector]
    public Rigidbody2D rigi2D;

    [Header("判断")]
    public bool canMove;
    public bool hasDashed;
    public bool isDashing;
    public bool wallGrab;
    public bool wallSlide;
    public bool wallJumped;
    private bool groundTouch;

    public int side = 1;

    [Space]
    [Header("粒子系统")]
    public ParticleSystem slidePartical;
    public ParticleSystem dashPartical;
    public ParticleSystem jumpPartical;
    public ParticleSystem wallJumpPartical;

    void Start()
    {
        rigi2D = GetComponent<Rigidbody2D>();
        anim = GetComponentInChildren<PlayerAnimation>();
        coll = GetComponent<PlayerCollision>();
    }
}

我们通过代码控制动画条件,有了我们前面的检测和PlayerMovement我们能更轻易的控制动画。

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

public class PlayerAnimation : MonoBehaviour
{
    private Animator anim;
    private PlayerMovement movement;
    private PlayerCollision coll;
    [HideInInspector]
    public SpriteRenderer sr;
    void Start()
    {
        anim = GetComponent<Animator>();
        coll = GetComponentInParent<PlayerCollision>();
        movement = GetComponentInParent<PlayerMovement>();
        sr = GetComponent<SpriteRenderer>();
    }

    void Update()
    {
        anim.SetBool("onWall", coll.onWall);
        anim.SetBool("onGround", coll.onGround);
        anim.SetBool("onRightWall", coll.onRightWall);
        anim.SetBool("wallGrab", movement.wallGrab);
        anim.SetBool("wallSlide", movement.wallSlide);
        anim.SetBool("canMove", movement.canMove);
        anim.SetBool("isDashing", movement.isDashing); ;
    }

    public void SetHorizontalMovement(float x,float y,float yVel)
    {
        anim.SetFloat("HorizontalAxis", x);
        anim.SetFloat("VerticalAxis", y);
        anim.SetFloat("VerticalVelocity", yVel);
    }

    public void SetATrigger(string trigger)
    {
        anim.SetTrigger(trigger);
    }

    public void Flip(int side)
    {
        if(movement.wallGrab || movement.wallSlide)
        {
            if (side == -1 && sr.flipX)
                return;

            if (side == 1 && !sr.flipX)
                return;
        }


        bool state = (side == 1) ? false : true;
        sr.flipX = state;
    }
}

然后在动画状态机上设置对应的参数以及连线设置条件【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

 【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感 【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

截屏截的有点多了,需要注意的是代码设置的动画名字和我们Animator的参数要对应起来

别忘了在我们的子对象上挂载脚本

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

接下来我们继续萹蓄PlayerMovement的脚本

首先是要在Update中持续读入的Input,并且要把参数传入到PlayerAnimation中让条件读入

void Update()
    {
        float x = Input.GetAxis("Horizontal");
        float y = Input.GetAxis("Vertical");
        float xRaw = Input.GetAxisRaw("Horizontal");
        float yRaw = Input.GetAxisRaw("Vertical");
        Vector2 dir = new Vector2(x, y);
        Walk(dir);
        anim.SetHorizontalMovement(x, y, rigi2D.velocity.y);
}

我们接着创建一个Walk(Vector2)函数,需要注意的是,为了让玩家在墙上跳跃的时候更加平滑即更好的操作空间,在玩家墙上跳跃的时候仍然能够往回走,就用了插值函数Lerp

 private void Walk(Vector2 dir)
    {
        if (!canMove)
            return;

        if (wallGrab)
            return;

        if (!wallJumped)
        {
            rigi2D.velocity = new Vector2(dir.x * moveSpeed, rigi2D.velocity.y);
        }
        else
        {
            rigi2D.velocity = Vector2.Lerp(rigi2D.velocity, new Vector2(dir.x * moveSpeed, rigi2D.velocity.y), wallJumpLefp * Time.deltaTime);
        }
    }

接下来编写跳跃相关的,跳跃分为在地面上跳跃以及再墙上跳跃,所以我们用我们的碰撞检测脚本的属性onWall和onGround来判断。

首先是在Update函数中

void Update
{
//跳跃
        if (Input.GetButtonDown("Jump"))
        {
            anim.SetATrigger("jump");

            if (coll.onGround)
            {
                Jump( Vector2.up,false);
            }else if (coll.onWall && !coll.onGround)
            {
                WallJump();
            }
        }
}
 private void Jump(Vector2 dir,bool wall)
    {
        slidePartical.transform.parent.localScale = new Vector3(ParticalSide(), 1, 1);
        ParticleSystem particle = wall ? wallJumpPartical : jumpPartical;

        rigi2D.velocity = new Vector2(rigi2D.velocity.x, 0);
        rigi2D.velocity += dir* jumpForece;

        particle.Play();
    }
private void WallJump()
    {
        if((coll.onRightWall && side ==1) || (coll.onRightWall && side == -1))
        {
            side *= -1;
            anim.Flip(side);
        }
        StopCoroutine(DisableMovement(0));
        StartCoroutine(DisableMovement(0.1f));

        Vector2 wallDir = coll.onRightWall ? Vector2.left : Vector2.right;

        Jump(Vector2.up /1.5f + wallDir /1.5f,true);

        wallJumped = true;
    }

接下来做Dash冲刺相关的函数,但首先要创建一个新脚本用来仿制玩家冲刺生成的残影,这里涉及的DG.Tweening都在我上一篇文章上讲过总的来说是先生成一个动画队列然后调用回调函数让动作按步骤执行并用循环3次生成3个残影。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class PlayerGhostTrail : MonoBehaviour
{
    private PlayerMovement movement;
    private PlayerAnimation anim;
    private SpriteRenderer sr;

    public Transform ghostParent;

    public Color trailColor;
    public Color fadeColor;
    public float ghostInterval;
    public float fadeTime;
    void Start()
    {
        movement = FindObjectOfType<PlayerMovement>();
        anim = FindObjectOfType<PlayerAnimation>();
        sr = GetComponent<SpriteRenderer>();
    }

    public void ShowGhost()
    {
        Sequence sequence = DOTween.Sequence();

        for (int i = 0; i < ghostParent.childCount; i++)
        {
            Transform currentGhost = ghostParent.GetChild(i);
            sequence.AppendCallback(() => currentGhost.transform.position = movement.transform.position);
            sequence.AppendCallback(() => currentGhost.GetComponent<SpriteRenderer>().flipX = anim.sr.flipX);
            sequence.AppendCallback(() => currentGhost.GetComponent<SpriteRenderer>().sprite = anim.sr.sprite);
            sequence.Append(currentGhost.GetComponent<SpriteRenderer>().material.DOColor(trailColor, 0));
            sequence.AppendCallback(() => FadeSprite(currentGhost));
            sequence.AppendInterval(ghostInterval);

        }
    }

    public void FadeSprite(Transform current)
    {
        current.GetComponent<SpriteRenderer>().material.DOKill();
        current.GetComponent<SpriteRenderer>().material.DOColor(fadeColor, fadeTime);
    }
}

然后再回到PlayerMovement脚本中

private void Dash(float x,float y)
    {
        Camera.main.transform.DOComplete(); //终止所有动画
        Camera.main.transform.DOShakePosition(0.2f, 0.5f, 14, 90, false, true); //这里运用的是我上一篇文章讲到的DOTween的让摄像机震动的效果
        FindObjectOfType<RippleEffect>().Emit(Camera.main.WorldToViewportPoint(transform.position)); //目的是使在玩家位置产生涟漪的效果,也是Github上的一个项目

        hasDashed = true;
        anim.SetATrigger("dash");

        rigi2D.velocity = Vector2.zero;
        Vector2 dir = new Vector2(x, y);

        rigi2D.velocity += dir.normalized * dashSpeed; //让冲刺朝着玩家移动的方向
        StartCoroutine(DashWait());
    }
    IEnumerator DashWait()
    {
        FindObjectOfType<PlayerGhostTrail>().ShowGhost();
        StartCoroutine(GroundDash());
        DOVirtual.Float(14, 0, 0.8f, RigibodyDrag);

        dashPartical.Play();
        rigi2D.gravityScale = 0;
        GetComponent<PlayerBetterJumping>().enabled = false;
        wallJumped = true;
        isDashing = true;

        yield return new WaitForSeconds(0.3f);

        dashPartical.Stop();
        isDashing = false;
        wallJumped = false;
        rigi2D.gravityScale = 3;
        GetComponent<PlayerBetterJumping>().enabled = true;
    }
    IEnumerator GroundDash()
    {
        yield return new WaitForSeconds(0.15f);
        if (coll.onGround)
        {
            hasDashed = false;
        }
    }

Update函数中需要写个判断条件

if(Input.GetButtonDown("Fire1")&& !hasDashed)
        {
            if(xRaw != 0 || yRaw != 0)
            {
                Dash(xRaw,yRaw);
            }
        }

接下来还要实现的还有墙上滑行,抓住墙壁,粒子效果,这里把完整的PlayerMovement贴上来

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;

public class PlayerMovement : MonoBehaviour
{
    [Space]
    [Header("运动参数")]
    [SerializeField] float moveSpeed;
    [SerializeField] float jumpForece;
    [SerializeField] float dashSpeed;
    [SerializeField] float slideSpeed;
    [SerializeField] float wallJumpLefp;

    private PlayerAnimation anim;
    private PlayerCollision coll;
    [HideInInspector]
    public Rigidbody2D rigi2D;

    [Header("判断")]
    public bool canMove;
    public bool hasDashed;
    public bool isDashing;
    public bool wallGrab;
    public bool wallSlide;
    public bool wallJumped;
    private bool groundTouch;

    public int side = 1;

    [Space]
    [Header("粒子系统")]
    public ParticleSystem slidePartical;
    public ParticleSystem dashPartical;
    public ParticleSystem jumpPartical;
    public ParticleSystem wallJumpPartical;

    void Start()
    {
        rigi2D = GetComponent<Rigidbody2D>();
        anim = GetComponentInChildren<PlayerAnimation>();
        coll = GetComponent<PlayerCollision>();
    }

    
    void Update()
    {
        float x = Input.GetAxis("Horizontal");
        float y = Input.GetAxis("Vertical");
        float xRaw = Input.GetAxisRaw("Horizontal");
        float yRaw = Input.GetAxisRaw("Vertical");
        Vector2 dir = new Vector2(x, y);
        Walk(dir);
        anim.SetHorizontalMovement(x, y, rigi2D.velocity.y);

        //抓住墙壁
        if (coll.onWall && Input.GetButton("Fire3") && canMove) 
        {
            if (side != coll.wallSide)
                anim.Flip(side * -1);
            wallGrab = true;
            wallSlide = false;
        }

        //放手
        if(!coll.onWall || !canMove || Input.GetButtonUp("Fire3"))
        {
            wallGrab = false;
            wallSlide = false;
        }

        if (coll.onGround && !isDashing)
        {
            wallJumped = false;
            GetComponent<PlayerBetterJumping>().enabled = true;
        }

        if(wallGrab && !isDashing)
        {
            rigi2D.gravityScale = 0;
            if(x>0.2f || x < -0.2f)
            {
                rigi2D.velocity = new Vector2(rigi2D.velocity.x, 0);
            }

            float speedModifier = y > 0 ? 0.5f : 1f;

            rigi2D.velocity = new Vector2(rigi2D.velocity.x,y * (moveSpeed * speedModifier));
        }
        else
        {
            rigi2D.gravityScale = 3;
        }

        //墙上滑行
        if(coll.onWall && !coll.onGround)
        {
            if (x != 0 && !wallGrab)
            {
                wallSlide = true;
                WallSlide();
            }
        }

        if(!coll.onWall || coll.onGround)
        {
            wallSlide = false;
        }

        //跳跃
        if (Input.GetButtonDown("Jump"))
        {
            anim.SetATrigger("jump");

            if (coll.onGround)
            {
                Jump( Vector2.up,false);
            }else if (coll.onWall && !coll.onGround)
            {
                WallJump();
            }
        }

        if(Input.GetButtonDown("Fire1")&& !hasDashed)
        {
            if(xRaw != 0 || yRaw != 0)
            {
                Dash(xRaw,yRaw);
            }
        }

        if(coll.onGround && !groundTouch)
        {
            GroundTouch();
            groundTouch = true;
        }

        if(!coll.onGround && groundTouch)
        {
            groundTouch = false;
        }

        WallPartical(y);

        if (wallSlide || wallGrab || !canMove)
            return;

        if(x>0)
        {
            side = 1;
            anim.Flip(side);
        }
        if (x < 0)
        {
            side = -1;
            anim.Flip(side);
        }
    }

    private void Walk(Vector2 dir)
    {
        if (!canMove)
            return;

        if (wallGrab)
            return;

        if (!wallJumped)
        {
            rigi2D.velocity = new Vector2(dir.x * moveSpeed, rigi2D.velocity.y);
        }
        else
        {
            rigi2D.velocity = Vector2.Lerp(rigi2D.velocity, new Vector2(dir.x * moveSpeed, rigi2D.velocity.y), wallJumpLefp * Time.deltaTime);
        }
    }
    private void Jump(Vector2 dir,bool wall)
    {
        slidePartical.transform.parent.localScale = new Vector3(ParticalSide(), 1, 1);
        ParticleSystem particle = wall ? wallJumpPartical : jumpPartical;

        rigi2D.velocity = new Vector2(rigi2D.velocity.x, 0);
        rigi2D.velocity += dir* jumpForece;

        particle.Play();
    }
    void GroundTouch()
    {
        hasDashed = false;
        isDashing = false;

        side = anim.sr.flipX ? -1 : 1;

        jumpPartical.Play();
    }
    private void Dash(float x,float y)
    {
        Camera.main.transform.DOComplete(); //终止所有动画
        Camera.main.transform.DOShakePosition(0.2f, 0.5f, 14, 90, false, true); //这里运用的是我上一篇文章讲到的DOTween的让摄像机震动的效果
        FindObjectOfType<RippleEffect>().Emit(Camera.main.WorldToViewportPoint(transform.position)); //目的是使在玩家位置产生涟漪的效果,也是Github上的一个项目

        hasDashed = true;
        anim.SetATrigger("dash");

        rigi2D.velocity = Vector2.zero;
        Vector2 dir = new Vector2(x, y);

        rigi2D.velocity += dir.normalized * dashSpeed; //让冲刺朝着玩家移动的方向
        StartCoroutine(DashWait());
    }
    IEnumerator DashWait()
    {
        FindObjectOfType<PlayerGhostTrail>().ShowGhost();
        StartCoroutine(GroundDash());
        DOVirtual.Float(14, 0, 0.8f, RigibodyDrag);

        dashPartical.Play();
        rigi2D.gravityScale = 0;
        GetComponent<PlayerBetterJumping>().enabled = false;
        wallJumped = true;
        isDashing = true;

        yield return new WaitForSeconds(0.3f);

        dashPartical.Stop();
        isDashing = false;
        wallJumped = false;
        rigi2D.gravityScale = 3;
        GetComponent<PlayerBetterJumping>().enabled = true;
    }
    IEnumerator GroundDash()
    {
        yield return new WaitForSeconds(0.15f);
        if (coll.onGround)
        {
            hasDashed = false;
        }
    }
    void WallPartical(float vertical)
    {
        var main = slidePartical.main;

        if(wallSlide || (wallGrab && vertical < 0))
        {
            slidePartical.transform.parent.localScale = new Vector3(ParticalSide(), 1, 1);
            main.startColor = Color.white;
        }
        else
        {
            main.startColor = Color.clear;
        }
    }

    private void WallJump()
    {
        if((coll.onRightWall && side ==1) || (coll.onRightWall && side == -1))
        {
            side *= -1;
            anim.Flip(side);
        }
        StopCoroutine(DisableMovement(0));
        StartCoroutine(DisableMovement(0.1f));

        Vector2 wallDir = coll.onRightWall ? Vector2.left : Vector2.right;

        Jump(Vector2.up /1.5f + wallDir /1.5f,true);

        wallJumped = true;
    }
    private void WallSlide()
    {
        if(coll.wallSide != side)
        {
            anim.Flip(side * -1);
        }

        if (!canMove)
            return;

        bool pushingWall = false;
        if((rigi2D.velocity.x > 0 && coll.onLeftWall) || (rigi2D.velocity.x<0 && coll.onRightWall))
        {
            pushingWall = true;
        }
        float push = pushingWall ? 0 : rigi2D.velocity.x;

        rigi2D.velocity = new Vector2(push, -slideSpeed);
    }
    IEnumerator DisableMovement(float time)
    {
        canMove = false;
        yield return new WaitForSeconds(time);
        canMove = true;
    }
    void RigibodyDrag(float x)
    {
        rigi2D.drag = x;
    }
    int ParticalSide()
    {
        int particalSide = coll.onRightWall ? 1 : -1;
        return particalSide;
    }
}

除此之外还需要一个脚本来使玩家能大小跳

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

public class PlayerBetterJumping : MonoBehaviour
{
    private Rigidbody2D rigi2D;
    public float fallMultipler = 2.5f;
    public float lowJumpMultipler = 2f;
    void Start()
    {
        rigi2D = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        if(rigi2D.velocity.y < 0)
        {
            rigi2D.velocity += Vector2.up * Physics2D.gravity.y * (fallMultipler - 1) * Time.deltaTime;
        }
        else if(rigi2D.velocity.y > 0 && !Input.GetButton("Jump"))
        {
            rigi2D.velocity += Vector2.up * Physics2D.gravity.y * (lowJumpMultipler - 1) * Time.deltaTime;
        }
    }
}

这些是所需的所有脚本,其中RippleEffect是玩家需要从源码中下载的。

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

别忘了给人物填上参数。

 【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感 

 以及给我们负责绘制Ground的一个层级【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

 还有我们的Ghost【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

 作者用一个名字叫"Ghost"的Material能让它们隐身,当使用的是会显示出来【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

使用作者的Partical System模拟出飘雪的感觉

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感 最后别忘了在Project Settings上更改你的Fire1和Fire3

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

 文章来源地址https://www.toymoban.com/news/detail-428030.html

 


学习产出:

首先是按住Fire1能够悬停在墙上 

【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感

冲刺的残影这个nm我截不到,按不到暂停所有就算了

总之残影和涟漪效果都是可以实现的。

 

到了这里,关于【Unity好项目分享】如何制作如游戏蔚蓝般极佳的操作手感的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包