英文:
2d Unity Shooting Bug Where The Player Shoots More Than The Cap Should Allow
问题
这个Unity C# 2D玩家控制器脚本使玩家角色能够在必须站在地面上重新加载和射击之前连续射击两次。问题是玩家可以射击一次,等待片刻,然后连续射击三次,而上限应为两次,然后他们需要重新加载才能再次射击。任何见解或帮助将不胜感激。
using System.Collections;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public bool enableDebugLogs = false;
public bool isGrounded;
public bool isReloading;
public bool isPlayingWindSound;
public bool isFalling;
public bool canMoveFreely;
public float recoilForce = 10.0f;
public float moveSpeed = 5.0f;
public float maxSpeed = 10.0f;
public float windSoundDelay = 1.0f;
public float shotCooldown = 0.1f;
public float timeFalling = 0.0f;
public float groundedTime = 0.0f;
public float groundedDuration = 0.2f;
public float freeMoveDuration = 1.0f;
public float idleTimeThreshold = 1.0f;
public float reloadTime = 0.1f;
public Camera mainCamera;
public Transform groundCheck;
public Vector2 groundCheckSize;
public LayerMask groundLayer;
public int maxShotsBeforeReload = 2;
public int shotsFired;
public AudioClip shootSound;
public AudioClip windSound;
public AudioSource audioSource;
public AudioSource windAudioSource;
public Texture2D crosshairTexture;
private Vector3 savedPosition;
private Rigidbody2D rb;
private SpriteRenderer spriteRenderer;
private Animator animator;
private float lastShotTime;
private float lastMouseMovementTime;
private PlatformMovement currentPlatform;
// ... 这里是其他函数,未包括在翻译中 ...
}
请注意,我只翻译了您提供的代码部分,没有包括其他内容。如果您需要更多帮助或有其他问题,请随时提出。
英文:
This Unity C# 2D Player Controller script has it so the player character is able to shoot twice before they have to be on the ground to reload and shoot again. The problem is that the player can shoot once, wait a few moments, and then shoot three times in a row when the cap should be two times and then they need to reload before they can shoot again. Any insight or help will be much appreciated.
using System.Collections;
using UnityEngine;
public class PlayerController : MonoBehaviour
{
public bool enableDebugLogs = false;
public bool isGrounded;
public bool isReloading;
public bool isPlayingWindSound;
public bool isFalling;
public bool canMoveFreely;
public float recoilForce = 10.0f;
public float moveSpeed = 5.0f;
public float maxSpeed = 10.0f;
public float windSoundDelay = 1.0f;
public float shotCooldown = 0.1f;
public float timeFalling = 0.0f;
public float groundedTime = 0.0f;
public float groundedDuration = 0.2f;
public float freeMoveDuration = 1.0f;
public float idleTimeThreshold = 1.0f;
public float reloadTime = 0.1f;
public Camera mainCamera;
public Transform groundCheck;
public Vector2 groundCheckSize;
public LayerMask groundLayer;
public int maxShotsBeforeReload = 2;
public int shotsFired;
public AudioClip shootSound;
public AudioClip windSound;
public AudioSource audioSource;
public AudioSource windAudioSource;
public Texture2D crosshairTexture;
private Vector3 savedPosition;
private Rigidbody2D rb;
private SpriteRenderer spriteRenderer;
private Animator animator;
private float lastShotTime;
private float lastMouseMovementTime;
private PlatformMovement currentPlatform;
public void Initialize(int shotsFired, bool isReloading)
{
this.shotsFired = shotsFired;
this.isReloading = isReloading;
}
private void Start()
{
rb = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
animator = GetComponent<Animator>();
}
private void FixedUpdate()
{
isGrounded = Physics2D.OverlapBox(groundCheck.position, groundCheckSize, 0, groundLayer);
if (rb.velocity.y < 0)
{
timeFalling += Time.fixedDeltaTime;
if (timeFalling >= windSoundDelay && !isPlayingWindSound && !isFalling)
{
isFalling = true;
animator.SetBool("IsFalling", isFalling);
Debug.Log("IsFalling set to true");
if (!windAudioSource.isPlaying)
{
windAudioSource.clip = windSound;
windAudioSource.Play();
isPlayingWindSound = true;
}
}
if (isPlayingWindSound)
{
float volumeFactor = Mathf.Clamp(timeFalling / 10.0f, 0.0f, 1.0f);
windAudioSource.volume = volumeFactor;
}
}
else if (isGrounded)
{
timeFalling = 0.0f;
if (isFalling)
{
isFalling = false;
animator.SetBool("IsFalling", isFalling);
animator.SetBool("IsLayingDown", true);
}
if (isPlayingWindSound)
{
StartCoroutine(FadeOutWindSound(0.2f));
}
}
if (shotsFired >= maxShotsBeforeReload && !isReloading && isGrounded)
{
isReloading = true;
StartCoroutine(Reload());
}
if (isGrounded)
{
groundedTime += Time.fixedDeltaTime;
}
else
{
groundedTime = 0.0f;
}
Vector2 mousePosition = mainCamera.ScreenToWorldPoint(Input.mousePosition);
Vector2 shotDirection = (mousePosition - (Vector2)transform.position).normalized;
float facingDirection = 1.0f;
if (spriteRenderer.flipX)
{
facingDirection = -1.0f;
}
animator.SetFloat("LookHorizontal", shotDirection.x * facingDirection);
animator.SetFloat("LookVertical", shotDirection.y);
if (animator.GetBool("IsLayingDown") == false)
{
if (shotDirection.x > 0)
{
spriteRenderer.flipX = false;
}
else if (shotDirection.x < 0)
{
spriteRenderer.flipX = true;
}
}
float horizontalInput = Input.GetAxis("Horizontal");
Debug.Log("Horizontal Input: " + horizontalInput); // Debug code
animator.SetFloat("Speed", Mathf.Abs(horizontalInput));
if (Mathf.Abs(horizontalInput) > 0 && isGrounded) // if there is any horizontal input and the player is grounded
{
animator.SetBool("IsLayingDown", false); // set IsLayingDown to false
}
if (Input.GetMouseButtonDown(0) && shotsFired < maxShotsBeforeReload && !isReloading && !animator.GetBool("IsLayingDown") && Time.time - lastShotTime >= shotCooldown)
{
shotsFired++;
lastShotTime = Time.time; // Update the time of the last shot
rb.AddForce(-shotDirection * recoilForce, ForceMode2D.Impulse);
animator.SetBool("IsRecoiling", true);
StartCoroutine(StopRecoilAnimation());
audioSource.PlayOneShot(shootSound);
}
if ((isGrounded || canMoveFreely))
{
Vector2 horizontalForce = new Vector2(horizontalInput * moveSpeed, 0);
Debug.Log("Horizontal Force: " + horizontalForce); // Debug code
rb.AddForce(horizontalForce, ForceMode2D.Force);
}
if (rb.velocity.magnitude > maxSpeed)
{
rb.velocity = rb.velocity.normalized * maxSpeed;
}
animator.SetBool("IsGrounded", isGrounded);
// Check for mouse movement
if (Input.GetAxis("Mouse X") != 0 || Input.GetAxis("Mouse Y") != 0)
{
lastMouseMovementTime = Time.time;
animator.SetBool("MouseMoving", true);
}
else
{
animator.SetBool("MouseMoving", false);
}
// Check if the time since the last mouse movement exceeds the threshold
if (Time.time - lastMouseMovementTime > idleTimeThreshold)
{
animator.SetBool("IsIdle", true);
}
else
{
animator.SetBool("IsIdle", false);
}
Debug.Log("Player Velocity: " + rb.velocity); // Debug code
}
private IEnumerator FadeOutWindSound(float duration)
{
float startVolume = windAudioSource.volume;
float elapsedTime = 0.0f;
while (windAudioSource.volume > 0.0f)
{
elapsedTime += Time.fixedDeltaTime;
windAudioSource.volume = Mathf.Lerp(startVolume, 0.0f, elapsedTime / duration);
yield return null;
}
windAudioSource.Stop();
isPlayingWindSound = false;
}
public void EnableFreeMovement()
{
canMoveFreely = true;
StartCoroutine(DisableFreeMovement());
}
private IEnumerator DisableFreeMovement()
{
yield return new WaitForSeconds(freeMoveDuration);
canMoveFreely = false;
}
private IEnumerator Reload()
{
isReloading = true;
Debug.Log("Reloading started.");
yield return new WaitForSeconds(reloadTime);
shotsFired = 0;
isReloading = false;
Debug.Log("Reloading finished. Current shot count: " + shotsFired);
}
private IEnumerator StopRecoilAnimation()
{
yield return new WaitForSeconds(animator.GetCurrentAnimatorStateInfo(0).length);
animator.SetBool("IsRecoiling", false);
}
private void OnDrawGizmos()
{
Gizmos.color = Color.red;
Gizmos.DrawWireCube(groundCheck.position, groundCheckSize);
}
private void OnCollisionEnter2D(Collision2D collision)
{
PlatformMovement platformMovement = collision.gameObject.GetComponent<PlatformMovement>();
if (platformMovement)
{
currentPlatform = platformMovement;
}
}
private void OnCollisionExit2D(Collision2D collision)
{
PlatformMovement platformMovement = collision.gameObject.GetComponent<PlatformMovement>();
if (platformMovement && platformMovement == currentPlatform)
{
currentPlatform = null;
}
}
}
答案1
得分: 1
总体而言,逻辑似乎是正确的,但有一些地方可以显著改进。虽然您的问题涉及技术方面,但似乎主要问题在于您的代码组织。
首先,我想提出一个小问题。在FixedUpdate
函数内处理输入可能会导致帧之间的不一致性和不精确性。有些帧会处理输入,而其他帧则不会。因此,建议考虑使用Update()
。有时仅仅通过更改它就可以解决这个问题。
以下是一些增强您代码的提示:
清理和简化
仔细查看您的代码,以逻辑方式进行简化。不要使用shotsFired
和maxShotsBeforeReload
,而是考虑使用更具描述性的变量名称,如magazineCount
和magazineCap
,或者甚至更简单的选项,如bulletsLeft
和bulletsMax
。采用这种方法将导致更一致的逻辑。
组织您的代码
通过引入方法并在必要时修改其中的代码来减少复杂的逻辑。从逻辑上讲,首先检查shotsFired >= maxShotsBeforeReload
是否有意义?也许在玩家射击后立即检查他们是否达到了最大子弹数更合乎逻辑。否则,他们将不得不等待另一个固定的更新。
引入局部变量
表达式如Time.time - lastShotTime >= shotCooldown
可能难以阅读和维护。考虑引入一个局部布尔变量,如onCooldown = (Time.time - lastShotTime <= shotCooldown)
。这将提高可读性并使您的代码更易管理。
希望这些建议能帮助您在代码的复杂结构中找到合适的解决方案。如有需要,请随时提出进一步的问题。
英文:
Overall, the logic appears to be sound, but there are several areas where it could be significantly improved. While your question pertains to technical aspects, it seems that the main issue lies in the organization of your code.
Firstly, I want to address a minor concern. Processing input inside the FixedUpdate
function can lead to inconsistencies and imprecisions across frames. Some frames will process input, while others won't. Therefore, it is advisable to consider Update()
. It can sometimes work just by changing that.
Here are some tips to enhance your code:
Clean up and simplify
Take a closer look at your code and aim to simplify it logically. Instead of using shotsFired
and maxShotsBeforeReload
, consider using more descriptive variable names like magazineCount
and magazineCap
, or even simpler options like bulletsLeft
and bulletsMax
. Adopting this approach will lead to more consistent logic.
Organize your code
Reduce complex logic by introducing methods and modifying the code within them if necessary. Logically, does it make sense to check if shotsFired >= maxShotsBeforeReload
first? It may be more logical to check if the player has reached the maximum number of bullets right after they shoot. Otherwise, they would have to wait for another fixed update.
Introduce local variables
Expressions like Time.time - lastShotTime >= shotCooldown
can be difficult to read and maintain. Consider introducing a local boolean variable like onCooldown = (Time.time - lastShotTime <= shotCooldown)
. This will improve readability and make your code more manageable.
I hope these suggestions help you navigate through the complex structure of your code and find a suitable solution. Don't hesitate to ask further questions if needed.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论