Unity3D仿星露谷物语开发17之空库存栏UI
1、目标
将库存栏放在游戏界面中,一般情况下角色居中展示时库存栏在底部,当角色位于界面下方时库存栏展示在顶部避免遮挡。
2、CanvasGroup组件
用于集中控制UI元素的透明度、交互性和射线投射行为。CanvasGroup的Alpha属性允许渐变效果,Interactable决定元素是否可交互,BlocksRaycasts影响图形射线检测,而IgnoreParentGroups选项则控制是否忽略父对象的CanvasGroup设置。这些特性在创建互动游戏场景时尤为有用。
3、Aspect Ratio Fitter组件
Aspect Ratio Fitter 组件的核心作用是按照给定的宽高比调整 UI 元素的尺寸。它可以根据宽度调整高度,也可以根据高度调整宽度,从而确保 UI 元素的宽高比始终固定,不会因为外部布局的变化而拉伸或压缩。
它有以下几种模式:
- None:不做任何宽高比调整,元素保持原始尺寸。
- Width Controls Height:根据宽度来调整高度,确保元素的宽高比保持不变。
- Height Controls Width:根据高度来调整宽度,以确保比例正确。
- Fit In Parent:在父容器内缩放元素,保持宽高比不变,同时确保元素的大小适合父容器。
- Envelope Parent:让元素包裹住父容器,保持比例,同时元素可能会超过父容器的边界。
这个属性用于设置 UI 元素的目标宽高比。通常,宽高比是通过 宽度 / 高度
的形式表示。例如,如果想保持 16:9 的比例,则需要将 Aspect Ratio
设置为 16 / 9
。
4、创建UI
在Hierarchy -> PersistentScene中创建空对象命名为UI。
在UI下创建Canvas命名为MainGameUICanvas。Canvas是UI组件的容器。相关属性设置如下:
在MainGameUICanvas下创建空物体命名为UICanvasGroup,添加两个组件Canvas Group和Aspect Ratio Fitter。
在UICanvasGroup下创建空物体命名为UIInventoryBar。
添加Image组件,并设置下列属性。
对于位置信息,按Shift + Alt键选中中间底部,此时Pos X/Y/Z均为0,修改Pos Y为2.5。
(Shift -> Shift + Alt键)
效果如下:
5、编写脚本
在Assets -> Scripts下新增UI目录,同时新增UIInventory子目录,在此目录下新增UIInventorybar.cs脚本。
(1)优化Player类
首先,我们需要根据角色的位置判断bar到底是处于底部还是顶部,所以Player类需要对外提供坐标信息,而且是相对屏幕的坐标。
修改Player.cs如下:
private Camera mainCamera;
mainCamera = Camera.main;
public Vector3 GetPlayerViewportPosition()
{
// Vector3 viewport position for player (0,0) viewport bottom left, (1,1) viewport top right
return mainCamera.WorldToViewportPoint(gameObject.transform.position);
}
此时Player.cs完整代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : SingletonMonobehaviour<Player>
{
private float xInput;
private float yInput;
private bool isWalking;
private bool isRunning;
private bool isIdle;
private bool isCarrying = false;
private ToolEffect toolEffect = ToolEffect.none;
private bool isUsingToolRight;
private bool isUsingToolLeft;
private bool isUsingToolUp;
private bool isUsingToolDown;
private bool isLiftingToolRight;
private bool isLiftingToolLeft;
private bool isLiftingToolUp;
private bool isLiftingToolDown;
private bool isPickingRight;
private bool isPickingLeft;
private bool isPickingUp;
private bool isPickingDown;
private bool isSwingToolRight;
private bool isSwingToolLeft;
private bool isSwingToolUp;
private bool isSwingToolDown;
private Camera mainCamera;
private Rigidbody2D rigidbody2D;
private Direction playerDirection;
private float movementSpeed;
private bool _playerInputIsDisabled = false;
public bool PlayerInputIsDisabled { get => _playerInputIsDisabled; set => _playerInputIsDisabled = value; }
protected override void Awake()
{
base.Awake();
rigidbody2D = GetComponent<Rigidbody2D>();
mainCamera = Camera.main;
}
private void Update()
{
#region Player Input
ResetAnimationTrigger();
PlayerMovementInput();
PlayerWalkInput();
// Send event to any listeners for player movement input
EventHandler.CallMovementEvent(xInput, yInput, isWalking, isRunning, isIdle, isCarrying, toolEffect,
isUsingToolRight, isUsingToolLeft, isUsingToolUp, isUsingToolDown,
isLiftingToolRight, isLiftingToolLeft, isLiftingToolUp, isLiftingToolDown,
isPickingRight, isPickingLeft, isPickingUp, isPickingDown,
isSwingToolRight, isSwingToolLeft, isSwingToolUp, isSwingToolDown,
false, false, false, false);
#endregion
}
private void FixedUpdate()
{
PlayerMovement();
}
private void PlayerMovement()
{
Vector2 move = new Vector2(xInput * movementSpeed * Time.deltaTime, yInput * movementSpeed * Time.deltaTime);
rigidbody2D.MovePosition(rigidbody2D.position + move);
}
private void ResetAnimationTrigger()
{
toolEffect = ToolEffect.none;
isUsingToolRight = false;
isUsingToolLeft = false;
isUsingToolUp = false;
isUsingToolDown = false;
isLiftingToolRight = false;
isLiftingToolLeft = false;
isLiftingToolUp = false;
isLiftingToolDown = false;
isPickingRight = false;
isPickingLeft = false;
isPickingUp = false;
isPickingDown = false;
isSwingToolRight = false;
isSwingToolLeft = false;
isSwingToolUp = false;
isSwingToolDown = false;
}
private void PlayerMovementInput()
{
xInput = Input.GetAxisRaw("Horizontal");
yInput = Input.GetAxisRaw("Vertical");
// 斜着移动
if (xInput != 0 && yInput != 0)
{
xInput = xInput * 0.71f;
yInput = yInput * 0.71f;
}
// 在移动
if (xInput != 0 || yInput != 0)
{
isRunning = true;
isWalking = false;
isIdle = false;
movementSpeed = Settings.runningSpeed;
// Capture player direction for save game
if (xInput < 0)
{
playerDirection = Direction.left;
}
else if (xInput > 0)
{
playerDirection = Direction.right;
}
else if (yInput < 0)
{
playerDirection = Direction.down;
}
else
{
playerDirection = Direction.up;
}
}
else if(xInput == 0 && yInput == 0)
{
isRunning = false;
isWalking = false;
isIdle = true;
}
}
// 按住Shift键移动为walk
private void PlayerWalkInput()
{
if(Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
{
isRunning = false;
isWalking = true;
isIdle = false;
movementSpeed = Settings.walkingSpeed;
}
else
{
isRunning = true;
isWalking = false;
isIdle = false;
movementSpeed = Settings.runningSpeed;
}
}
public Vector3 GetPlayerViewportPosition()
{
// Vector3 viewport position for player (0,0) viewport bottom left, (1,1) viewport top right
return mainCamera.WorldToViewportPoint(gameObject.transform.position);
}
}
(2)编写UIInventorybar类
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UIInventoryBar : MonoBehaviour
{
private RectTransform rectTransform;
private bool _isInventoryBarPositionBottom = true;
public bool IsInventoryBarPositionBottom { get { return _isInventoryBarPositionBottom;} set { _isInventoryBarPositionBottom = value; } }
private void Awake()
{
rectTransform = GetComponent<RectTransform>();
}
private void Update()
{
// Switch inventory bar position depending on player position
SwitchInventoryBarPosition();
}
private void SwitchInventoryBarPosition()
{
Vector3 playerViewportPosition = Player.Instance.GetPlayerViewportPosition();
if (playerViewportPosition.y > 0.3f && IsInventoryBarPositionBottom == false)
{
rectTransform.pivot = new Vector2(0.5f, 0f);
rectTransform.anchorMin = new Vector2(0.5f, 0f);
rectTransform.anchorMax = new Vector2(0.5f, 0f);
rectTransform.anchoredPosition = new Vector2(0f, 2.5f);
IsInventoryBarPositionBottom = true;
}
else if (playerViewportPosition.y <= 0.3f && IsInventoryBarPositionBottom == true)
{
rectTransform.pivot = new Vector2(0.5f, 1f);
rectTransform.anchorMin = new Vector2(0.5f, 1f);
rectTransform.anchorMax = new Vector2(0.5f, 1f);
rectTransform.anchoredPosition = new Vector2(0f, -2.5f);
IsInventoryBarPositionBottom = false;
}
}
}
以上参数的值对应Rect Transform的值,比如当位于底部时:
在顶部时对应的值:
将UIInventoryBar脚本添加到UIInventoryBar对象上。
运行程序,效果如下: