最終效果
文章目錄
- 最終效果
- 素材下載
- 人物
- 環境
- 簡單繪制環境
- 角色移動跳躍
- 視差和攝像機跟隨效果
- 奔跑動畫切換
- 跳躍動畫,跳躍次數限制
- 角色添加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(); // 如果按下跳躍鍵,則執行跳躍}// 檢測是否松開Jumpif (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是生活!如果你遇到任何問題,也歡迎你評論私信找我, 雖然有些問題我也不一定會,但是我會查閱各方資料,爭取給出最好的建議,希望可以幫助更多想學編程的人,共勉~