最终效果
文章目录
- 最终效果
- 素材下载
- 人物
- 环境
- 简单绘制环境
- 角色移动跳跃
- 视差和摄像机跟随效果
- 奔跑动画切换
- 跳跃动画,跳跃次数限制
- 角色添加2d物理材质,防止角色粘在墙上
- 如果角色移动时背景出现黑线条
- 方法一
- 方法二
- 墙壁滑行
- 实现角色滑墙不可以通过移动离开且不可翻转角色
- 空中运动控制
- 可变跳跃高度
- 蹬墙跳
- 完整代码
- 源码
- 完结
素材下载
人物
https://rvros.itch.io/animated-pixel-hero
环境
https://bardent.itch.io/the-bardent-asset-pack
https://brullov.itch.io/oak-woods
简单绘制环境
参考:【推荐100个unity插件之14】Unity2D TileMap的探究(最简单,最全面的TileMap使用介绍)
角色移动跳跃
新增PlayerController
public class PlayerController : MonoBehaviour
{
private float movementInputDirection; // 水平输入方向
private bool isFacingRight = true; // 玩家是否面向右侧
private Rigidbody2D rb;
public float movementSpeed = 10.0f; // 移动速度
public float jumpForce = 16.0f; // 跳跃力度
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
CheckInput(); // 检查输入
CheckMovementDirection();
}
private void FixedUpdate()
{
ApplyMovement(); // 应用移动
}
// 检查玩家面朝的方向
private void CheckMovementDirection()
{
if (isFacingRight && movementInputDirection < 0)
{
Flip(); // 翻转角色
}
else if (!isFacingRight && movementInputDirection > 0)
{
Flip(); // 翻转角色
}
}
// 检查输入
private void CheckInput()
{
movementInputDirection = Input.GetAxisRaw("Horizontal"); // 获取水平输入
if (Input.GetButtonDown("Jump"))
{
Jump(); // 如果按下跳跃键,则执行跳跃
}
}
// 跳跃
private void Jump()
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce); // 设置 y 方向的速度为跳跃力度
}
// 移动
private void ApplyMovement()
{
rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
}
// 翻转角色
private void Flip()
{
isFacingRight = !isFacingRight; // 改变面向方向的标志
transform.Rotate(0.0f, 180.0f, 0.0f); // 旋转角色
}
}
配置
效果
视差和摄像机跟随效果
参考:【unity小技巧】Unity实现视差效果与无限地图
新增CameraController代码
public class CameraController : MonoBehaviour
{
public Transform target;//玩家的位置
public Transform farBackground, middleBackground, frontBackground;//远的背景和中间背景的位置
private Vector2 lastPos;//最后一次的相机位置
void Start()
{
lastPos = transform.position;//记录相机的初始位置
}
void Update()
{
//将相机的位置设置为玩家的位置,但限制在一定的垂直范围内
//transform.position = new Vector3(target.position.x, target.position.y + 1f, transform.position.z);
//计算相机在上一帧和当前帧之间移动的距离
Vector2 amountToMove = new Vector2(transform.position.x - lastPos.x, transform.position.y - lastPos.y);
//根据相机移动的距离,移动远背景和中间背景的位置
farBackground.position += new Vector3(amountToMove.x, amountToMove.y, 0f);
middleBackground.position += new Vector3(amountToMove.x * 0.9f, amountToMove.y, 0f);
frontBackground.position += new Vector3(amountToMove.x * 0.5f, amountToMove.y, 0f);
lastPos = transform.position;//更新最后一次的相机位置
}
}
Map代码
public class Map : MonoBehaviour
{
[Header("无限地图")]
private GameObject mainCamera;//主摄像机对象
private float mapwidth;//地图宽度
private float totalwidth;//总地图宽度
public int mapNums;//地图重复的次数
void Start()
{
mainCamera = GameObject.FindGameObjectWithTag("MainCamera");//查找标签为"MainCamera'"的对象并赋值
mapwidth = GetComponent<SpriteRenderer>().sprite.bounds.size.x;//通过SpriteRenderer获得图像宽度
totalwidth = mapwidth * mapNums;//计算总地图宽度
}
void FixedUpdate()
{
Vector3 tempPosition = transform.position;//获取当前位置
if (mainCamera.transform.position.x > transform.position.x + totalwidth / 2)
{
tempPosition.x += totalwidth;//将地图向右平移一个完整的地图宽度
transform.position = tempPosition;//更新位置
}
else if (mainCamera.transform.position.x < transform.position.x - totalwidth / 2)
{
tempPosition.x -= totalwidth;//将地图向左平移一个完整的地图宽度
transform.position = tempPosition;//更新位置
}
}
}
配置
效果
奔跑动画切换
动画配置
修改PlayerController
private void FixedUpdate()
{
ApplyMovement(); // 应用移动
UpdateStatus();
}
//判断状态
private void UpdateStatus(){
if(rb.velocity.x != 0){
isRunning = true;
}else{
isRunning = false;
}
}
//播放动画
private void UpdateAnimations(){
animator.SetBool("isRunning", isRunning);
}
效果
跳跃动画,跳跃次数限制
配置跳跃动画
修改PlayerController
[Header("跳跃 地面检测")]
private float amountOfJumpsLeft;//当前可跳跃次数
private bool isGround;//是否是地面
private bool canJump;//能否跳跃
public int amountOfJumps = 1;//跳跃次数
public float groundCheckRadius;//地面检测距离
public Transform groundCheck;//地面检测点
public LayerMask whatIsGround;//地面检测图层
void Start()
{
rb = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
amountOfJumpsLeft = amountOfJumps;
}
// 检查输入
private void CheckInput()
{
movementInputDirection = Input.GetAxisRaw("Horizontal"); // 获取水平输入
if (Input.GetButtonDown("Jump"))
{
Jump(); // 如果按下跳跃键,则执行跳跃
}
}
// 跳跃
private void Jump()
{
if (canJump)
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce); // 设置 y 方向的速度为跳跃力度
amountOfJumpsLeft--;
}
}
//判断能否跳跃
private void CheckIfCanJump()
{
if (isGround && rb.velocity.y < 0)
{
amountOfJumpsLeft = amountOfJumps;
}
if (amountOfJumpsLeft <= 0)
{
canJump = false;
}
else
{
canJump = true;
}
}
//播放动画
private void UpdateAnimations()
{
animator.SetBool("isRunning", isRunning);
animator.SetBool("isGround", isGround);
animator.SetFloat("yVelocity", rb.velocity.y);
}
//检测
private void CheckSurroundings()
{
//地面检测
isGround = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
}
//视图显示检测范围
private void OnDrawGizmos()
{
Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
}
配置,这里配置了2段跳
记得配置地面图层为Ground
效果
角色添加2d物理材质,防止角色粘在墙上
修改摩檫力为0
配置
效果
如果角色移动时背景出现黑线条
方法一
添加材质
配置
方法二
我们创建了一个Sprite Atlas来将Spritesheet拖入其中
把你的瓦片素材拖入
墙壁滑行
配置滑墙动画
修改PlayerController
[Header("墙壁滑行")]
public float wallCheckDistance;//墙壁检测距离
public Transform wallCheck;//墙壁检测点
public float wallSlideSpeed;//墙壁滑行速度
private bool isTouchingWall;//是否接触墙壁
private bool isWallSliding;//是否正在墙壁滑行
// 移动
private void ApplyMovement()
{
rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
//应用滑墙速度
if (isWallSliding)
{
if (rb.velocity.y < -wallSlideSpeed)
{
rb.velocity = new Vector2(rb.velocity.x, -wallSlideSpeed);// 限制垂直速度以应用墙壁滑行速度
}
}
}
//是否正在墙壁滑行
private void CheckIfWallSliding()
{
if (isTouchingWall && !isGround && rb.velocity.y < 0)
{
isWallSliding = true;
}
else
{
isWallSliding = false;
}
}
//播放动画
private void UpdateAnimations()
{
animator.SetBool("isRunning", isRunning);
animator.SetBool("isGround", isGround);
animator.SetFloat("yVelocity", rb.velocity.y);
animator.SetBool("isWallSliding", isWallSliding);
}
//检测
private void CheckSurroundings()
{
//地面检测
isGround = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
//墙面检测
isTouchingWall = Physics2D.Raycast(wallCheck.position, transform.right, wallCheckDistance, whatIsGround);
}
//视图显示检测范围
private void OnDrawGizmos()
{
Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
Gizmos.DrawLine(wallCheck.position, wallCheck.position + wallCheckDistance * Vector3.right);
}
配置
效果
实现角色滑墙不可以通过移动离开且不可翻转角色
// 移动
private void ApplyMovement()
{
// 如果在地面上
if (isGround)
{
rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
}
//应用滑墙速度
if (isWallSliding)
{
if (rb.velocity.y < -wallSlideSpeed)
{
rb.velocity = new Vector2(rb.velocity.x, -wallSlideSpeed);// 限制垂直速度以应用墙壁滑行速度
}
}
}
// 翻转角色
private void Flip()
{
if (!isWallSliding)
{
isFacingRight = !isFacingRight; // 改变面向方向的标志
transform.Rotate(0.0f, 180.0f, 0.0f); // 旋转角色
}
}
效果
空中运动控制
空气阻力和移动速度控制
public float movementForceInAir;//空气中的运动力
public float airDragMultiplier = 0.95f;//空气阻力
// 移动
private void ApplyMovement()
{
// 如果在地面上
if (isGround)
{
rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
}
// 如果不在地面上且不是在墙壁滑行且有水平输入
else if (!isGround && !isWallSliding && movementInputDirection != 0)
{
Vector2 forceToAdd = new Vector2(movementForceInAir * movementInputDirection, 0);// 在空中施加的力
rb.AddForce(forceToAdd);// 添加力到刚体
if (Mathf.Abs(rb.velocity.x) > movementSpeed)
{
rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y);// 限制水平速度
}
}
// 如果不在地面上且不是在墙壁滑行且没有水平输入
else if (!isGround && !isWallSliding && movementInputDirection == 0)
{
rb.velocity = new Vector2(rb.velocity.x * airDragMultiplier, rb.velocity.y);// 应用空气阻力
}
//应用滑墙速度
if (isWallSliding)
{
if (rb.velocity.y < -wallSlideSpeed)
{
rb.velocity = new Vector2(rb.velocity.x, -wallSlideSpeed);// 限制垂直速度以应用墙壁滑行速度
}
}
}
配置
效果
可变跳跃高度
长跳跃和短跳,长按和之前跳的和之前一样高
public float variableJumpHeightMultiplier = 0.5f;
// 检查输入
private void CheckInput()
{
movementInputDirection = Input.GetAxisRaw("Horizontal"); // 获取水平输入
if (Input.GetButtonDown("Jump"))
{
Jump(); // 如果按下跳跃键,则执行跳跃
}
// 检测是否松开Jump
if (Input.GetButtonUp("Jump"))
{
rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * variableJumpHeightMultiplier);
}
}
效果
蹬墙跳
实现不输入,点击跳跃就从墙上跳下来,方向按键+跳跃控制左右蹬墙跳
[Header("蹬墙跳")]
public float wallHopForce; // 离开墙时的力
public float wallJumpForce; // 蹬墙跳时施加的力
public Vector2 wallHopDirection; // 离开墙时的方向向量
public Vector2 wallJumpDirection; // 蹬墙跳时的方向向量
private int facingDirection = 1; // 角色面向的方向,1右 -1左
void Start()
{
rb = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
amountOfJumpsLeft = amountOfJumps;
//归一化向量,因为我们只要向量的方向,而不考虑长度
wallHopDirection = wallHopDirection.normalized;
wallJumpDirection = wallJumpDirection.normalized;
}
// 跳跃
private void Jump()
{
// 如果可以跳跃并且不是在墙壁滑行状态下
if (canJump && !isWallSliding)
{
rb.velocity = new Vector2(rb.velocity.x, jumpForce); // 设置 y 方向的速度为跳跃力度
amountOfJumpsLeft--;
}
// 如果正在墙壁滑行且没有输入水平移动方向,并且可以跳跃
else if(isWallSliding && movementInputDirection == 0 && canJump){
isWallSliding = false;
amountOfJumpsLeft--;
// 计算添加的力量,使角色从墙壁上弹开
Vector2 forceToAdd = new Vector2(wallHopForce * wallHopDirection.x * -facingDirection, wallHopForce * wallHopDirection.y);
rb.AddForce(forceToAdd, ForceMode2D.Impulse);
}
// 如果正在墙壁滑行或者正在接触墙壁,并且有水平移动输入,并且可以跳跃
else if((isWallSliding || isTouchingWall) && movementInputDirection != 0 && canJump){
isWallSliding = false;
amountOfJumpsLeft --;
// 计算添加的力量,使角色从墙壁上跳跃
Vector2 forceToAdd = new Vector2(wallHopForce * wallHopDirection.x * movementInputDirection, wallJumpForce * wallJumpDirection.y);
rb.AddForce(forceToAdd, ForceMode2D.Impulse);
}
}
//判断能否跳跃
private void CheckIfCanJump()
{
if ((isGround && rb.velocity.y < 0) || isWallSliding)
{
amountOfJumpsLeft = amountOfJumps;
}
if (amountOfJumpsLeft <= 0)
{
canJump = false;
}
else
{
canJump = true;
}
}
// 翻转角色
private void Flip()
{
if (!isWallSliding)
{
facingDirection *= -1;
isFacingRight = !isFacingRight; // 改变面向方向的标志
transform.Rotate(0.0f, 180.0f, 0.0f); // 旋转角色
}
}
配置
运行效果
完整代码
using Unity.VisualScripting;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
private float movementInputDirection; // 水平输入方向
private bool isFacingRight = true; // 玩家是否面向右侧
private Rigidbody2D rb;
public float movementSpeed = 10.0f; // 移动速度
public float jumpForce = 16.0f; // 跳跃力度
private Animator animator;
[Header("状态")]
public bool isRunning;
[Header("跳跃 地面检测")]
public int amountOfJumps = 1;//跳跃次数
public float groundCheckRadius;//地面检测距离
public Transform groundCheck;//地面检测点
public LayerMask whatIsGround;//地面检测图层
private float amountOfJumpsLeft;//当前可跳跃次数
private bool isGround;//是否是地面
private bool canJump;//能否跳跃
[Header("墙壁滑行")]
public float wallCheckDistance;//墙壁检测距离
public Transform wallCheck;//墙壁检测点
public float wallSlideSpeed;//墙壁滑行速度
public float movementForceInAir;//空气中的运动力
public float airDragMultiplier = 0.95f;//空气阻力
private bool isTouchingWall;//是否接触墙壁
private bool isWallSliding;//是否正在墙壁滑行
[Header("可变高度")]
public float variableJumpHeightMultiplier = 0.5f;
[Header("蹬墙跳")]
public float wallHopForce; // 离开墙时的力
public float wallJumpForce; // 蹬墙跳时施加的力
public Vector2 wallHopDirection; // 离开墙时的方向向量
public Vector2 wallJumpDirection; // 蹬墙跳时的方向向量
private int facingDirection = 1; // 角色面向的方向,1右 -1左
void Start()
{
rb = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
amountOfJumpsLeft = amountOfJumps;
//归一化向量,因为我们只要向量的方向,而不考虑长度
wallHopDirection = wallHopDirection.normalized;
wallJumpDirection = wallJumpDirection.normalized;
}
void Update()
{
CheckInput(); // 检查输入
CheckMovementDirection();
UpdateAnimations();
CheckIfCanJump();
CheckIfWallSliding();
CheckSurroundings();
}
private void FixedUpdate()
{
ApplyMovement(); // 应用移动
UpdateStatus();
}
// 检查玩家面朝的方向
private void CheckMovementDirection()
{
if (isFacingRight && movementInputDirection < 0)
{
Flip(); // 翻转角色
}
else if (!isFacingRight && movementInputDirection > 0)
{
Flip(); // 翻转角色
}
}
// 检查输入
private void CheckInput()
{
movementInputDirection = Input.GetAxisRaw("Horizontal"); // 获取水平输入
if (Input.GetButtonDown("Jump"))
{
Jump(); // 如果按下跳跃键,则执行跳跃
}
if (Input.GetButtonUp("Jump"))
{
rb.velocity = new Vector2(rb.velocity.x, rb.velocity.y * variableJumpHeightMultiplier);
}
}
// 移动
private void ApplyMovement()
{
// 如果在地面上
if (isGround)
{
rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
}
// 如果不在地面上且不是在墙壁滑行且有水平输入
else if (!isGround && !isWallSliding && movementInputDirection != 0)
{
Vector2 forceToAdd = new Vector2(movementForceInAir * movementInputDirection, 0);// 在空中施加的力
rb.AddForce(forceToAdd);// 添加力到刚体
if (Mathf.Abs(rb.velocity.x) > movementSpeed)
{
rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y);// 限制水平速度
}
}
// 如果不在地面上且不是在墙壁滑行且没有水平输入
else if (!isGround && !isWallSliding && movementInputDirection == 0)
{
rb.velocity = new Vector2(rb.velocity.x * airDragMultiplier, rb.velocity.y);// 应用空气阻力
}
// //应用滑墙速度
if (isWallSliding)
{
if (rb.velocity.y < -wallSlideSpeed)
{
rb.velocity = new Vector2(rb.velocity.x, -wallSlideSpeed);// 限制垂直速度以应用墙壁滑行速度
}
}
}
// 移动
private void ApplyMovement()
{
if(!isGround && !isWallSliding && movementInputDirection == 0){
rb.velocity = new Vector2(rb.velocity.x * airDragMultiplier, rb.velocity.y);// 应用空气阻力
}else{
rb.velocity = new Vector2(movementSpeed * movementInputDirection, rb.velocity.y); // 设置 x 方向的速度
}
//应用滑墙速度
if (isWallSliding)
{
if (rb.velocity.y < -wallSlideSpeed)
{
rb.velocity = new Vector2(rb.velocity.x, -wallSlideSpeed);// 限制垂直速度以应用墙壁滑行速度
}
}
}
//判断跑步状态
private void UpdateStatus(){
if(rb.velocity.x != 0){
isRunning = true;
}else{
isRunning = false;
}
}
// 翻转角色
private void Flip()
{
if (!isWallSliding)
{
facingDirection *= -1;
isFacingRight = !isFacingRight; // 改变面向方向的标志
transform.Rotate(0.0f, 180.0f, 0.0f); // 旋转角色
}
}
//播放动画
private void UpdateAnimations()
{
animator.SetBool("isRunning", isRunning);
animator.SetBool("isGround", isGround);
animator.SetFloat("yVelocity", rb.velocity.y);
animator.SetBool("isWallSliding", isWallSliding);
}
//判断能否跳跃
private void CheckIfCanJump()
{
if ((isGround && rb.velocity.y < 0) || isWallSliding)
{
amountOfJumpsLeft = amountOfJumps;
}
if (amountOfJumpsLeft <= 0)
{
canJump = false;
}
else
{
canJump = true;
}
}
//检测
private void CheckSurroundings()
{
//地面检测
isGround = Physics2D.OverlapCircle(groundCheck.position, groundCheckRadius, whatIsGround);
//墙面检测
isTouchingWall = Physics2D.Raycast(wallCheck.position, transform.right, wallCheckDistance, whatIsGround);
}
//是否墙壁滑行
private void CheckIfWallSliding()
{
if (isTouchingWall && !isGround && rb.velocity.y < 0)
{
isWallSliding = true;
}
else
{
isWallSliding = false;
}
}
//视图显示检测范围
private void OnDrawGizmos()
{
Gizmos.DrawWireSphere(groundCheck.position, groundCheckRadius);
Gizmos.DrawLine(wallCheck.position, wallCheck.position + wallCheckDistance * Vector3.right);
}
}
源码
整理好了我会放上来
完结
赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注
,以便我第一时间收到反馈,你的每一次支持
都是我不断创作的最大动力。当然如果你发现了文章中存在错误
或者有更好的解决方法
,也欢迎评论私信告诉我哦!
好了,我是向宇
,https://xiangyu.blog.csdn.net
一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~