Unity中角色背景移动时金币DOTween动画目标位置偏移问题求助
问题描述
我正在开发一款主角以恒定速度持续移动并收集金币的游戏。收集金币时,通过DOTween动画将金币移动到屏幕上显示固定金币总数的文本位置。代码中通过Vector3 targetPosition = target.position获取目标位置,但由于角色和背景持续向前移动,金币最终会移动到目标位置后方。尝试过FixedUpdate与Update结合的解决方案,但没有效果。
当前代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; using TMPro; using DG.Tweening; using System.Collections.Generic; public class CoinCounter : MonoBehaviour { [Header("UI reference")] [SerializeField] TMP_Text coinUIText; [SerializeField] GameObject animatedCoinPrefab; [SerializeField] Transform target; [Space] [Header("Available coins : (coins to pool )")] [SerializeField] int maxCoins; Queue<GameObject> coinsQueue = new Queue<GameObject>(); [Space] [Header("Animation settings ")] [SerializeField] [Range(0.5f,0.9f)] float minAnimDuration; [SerializeField] [Range(0.9f,2f)] float maxAnimDuration; [SerializeField] Ease easeType; Vector3 targetPosition; private int _c = 0; public int Coins { get{return _c;} set{ _c = value; //update UI text whenever "Coins" variable is changeds coinUIText.text = Coins.ToString(); } } void FixedUpdate() { targetPosition = target.position; //prepare pool PrepareCoins(); } void PrepareCoins() { GameObject coin; for(int i = 0; i<maxCoins; i++) { coin = Instantiate(animatedCoinPrefab); coin.transform.parent = transform; coin.SetActive(false); coinsQueue.Enqueue(coin); } } void Animate(Vector3 collectedCoinPosition, int amount) { for(int i = 0; i<amount; i++) { //check if there's coins in the pool if(coinsQueue.Count > 0) { //extract a coin from the pool GameObject coin = coinsQueue.Dequeue(); coin.SetActive(true); //move coin to the collected coin pos coin.transform.position = collectedCoinPosition; //animate coin to target position float duration = Random.Range(minAnimDuration,maxAnimDuration); coin.transform.DOMove(targetPosition,duration).SetEase(easeType).OnComplete(()=> { //execute whenever coin reach target position coin.SetActive(false); coinsQueue.Enqueue(coin); Coins++; }); } } } public void AddCoins(Vector3 collectedCoinPosition, int amount) { Animate(collectedCoinPosition,amount); } // Update is called once per frame void OnTriggerEnter2D(Collider2D other) { } }
解决方案
1. 转换坐标系统:使用UI画布的局部坐标
目标是屏幕上固定的UI文本,不能用世界坐标作为动画终点。需要将金币的世界位置转换为UI画布的局部坐标,再让动画目标指向这个UI坐标。
2. 修复对象池初始化逻辑
原代码在FixedUpdate中反复调用PrepareCoins,会无限生成金币对象导致性能问题,应该只在Start方法中初始化一次对象池。
3. 避免动态更新目标位置
UI元素在屏幕上的位置固定,不需要在FixedUpdate中实时更新targetPosition,直接获取一次UI的局部坐标即可。
修改后的代码:
using System.Collections; using System.Collections.Generic; using UnityEngine; using TMPro; using DG.Tweening; public class CoinCounter : MonoBehaviour { [Header("UI reference")] [SerializeField] TMP_Text coinUIText; [SerializeField] GameObject animatedCoinPrefab; [SerializeField] RectTransform targetUIRect; // 改用RectTransform获取UI坐标 [SerializeField] Canvas canvas; // 引用UI画布 [Space] [Header("Available coins : (coins to pool )")] [SerializeField] int maxCoins; Queue<GameObject> coinsQueue = new Queue<GameObject>(); [Space] [Header("Animation settings ")] [SerializeField] [Range(0.5f,0.9f)] float minAnimDuration; [SerializeField] [Range(0.9f,2f)] float maxAnimDuration; [SerializeField] Ease easeType; private int _c = 0; public int Coins { get{return _c;} set{ _c = value; coinUIText.text = Coins.ToString(); } } void Start() { // 只在初始化时创建对象池 PrepareCoins(); } void PrepareCoins() { for(int i = 0; i<maxCoins; i++) { GameObject coin = Instantiate(animatedCoinPrefab); // 将动画金币设为画布子物体,使用UI坐标 coin.transform.SetParent(canvas.transform, false); coin.SetActive(false); coinsQueue.Enqueue(coin); } } void Animate(Vector3 collectedCoinWorldPosition, int amount) { // 将金币的世界坐标转换为UI画布的局部坐标 Vector2 targetUIPosition = RectTransformUtility.WorldToScreenPoint(Camera.main, targetUIRect.position); RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, targetUIPosition, canvas.worldCamera, out targetUIPosition); for(int i = 0; i<amount; i++) { if(coinsQueue.Count > 0) { GameObject coin = coinsQueue.Dequeue(); coin.SetActive(true); // 将收集到的金币世界坐标转成UI坐标 Vector2 coinUIPosition; RectTransformUtility.ScreenPointToLocalPointInRectangle(canvas.transform as RectTransform, Camera.main.WorldToScreenPoint(collectedCoinWorldPosition), canvas.worldCamera, out coinUIPosition); coin.GetComponent<RectTransform>().anchoredPosition = coinUIPosition; // 动画到UI目标位置 float duration = Random.Range(minAnimDuration,maxAnimDuration); coin.GetComponent<RectTransform>().DOAnchorPos(targetUIPosition, duration) .SetEase(easeType) .OnComplete(()=> { coin.SetActive(false); coinsQueue.Enqueue(coin); Coins++; }); } } } public void AddCoins(Vector3 collectedCoinPosition, int amount) { Animate(collectedCoinPosition, amount); } void OnTriggerEnter2D(Collider2D other) { // 这里可以添加检测金币的逻辑,调用AddCoins } }
关键修改说明
- 将动画金币改为UI元素的子物体,使用
RectTransform的锚点坐标进行动画,避免世界移动影响 - 使用
RectTransformUtility将世界坐标和屏幕坐标转换为UI画布的局部坐标,确保动画终点始终是屏幕上的固定位置 - 修复对象池初始化逻辑,只在Start中执行一次,避免内存泄漏
- 用
DOAnchorPos替代DOMove,适配UI元素的坐标系统
内容的提问来源于stack exchange,提问作者hakanmes
相关产品推荐
相关产品推荐

