I am making a little top-down 2D RPG right now and I'm struggling with the range of my attacks. The problem is that my range is stuck on the right side of my player. This is of course really annoying, because you can't attack to the left.
I want to make a script that makes the range transform, based on where you are facing. I'm just not experienced enough to know how.
Maybe creating a script that changes the whole movement of the player, wouldn't be a bad idea. It would of course be nicer to just add a few things to fix the problem, but I would also be ok with changing the overall movement as a solution, I guess.
Here is the Movement + PlayerAttack
script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
public class Move : MonoBehaviour
{
public int damage;
private float timeBtwAttack;
public float startTimeBtwAttack;
public float attackCooldown;
float _lastAttackTime;
public Transform attackPos;
public LayerMask whatIsEnemies;
public float attackRangeX;
public float attackRangeY;
public float ValueIsGiven;
//Move Start
[SerializeField] private Rigidbody2D rb2D;
[SerializeField] private float speed;
private Vector2 moveInputValue;
private void OnMove(InputValue value)
{
moveInputValue = value.Get<Vector2>();
if(ValueIsGiven >= 1){
Debug.Log(moveInputValue);
}
}
private void MoveLogicMethod()
{
Vector2 result = moveInputValue * speed * Time.fixedDeltaTime;
rb2D.velocity = result;
}
private void FixedUpdate()
{
MoveLogicMethod();
}
//Move End
//Attack Start
public void OnPlayerAttacking()
{
// If attacking too close to previous attack, ignore this input.
if (Time.time - _lastAttackTime < attackCooldown) return;
var enemiesToDamage = Physics2D.OverlapBoxAll(
attackPos.position,
new Vector2(attackRangeX, attackRangeY),
0,
whatIsEnemies
);
foreach (var enemy in enemiesToDamage) {
enemy.GetComponent<Enemy>().TakeDamage(damage);
}
_lastAttackTime = Time.time;
}
void OnDrawGizmosSelected(){
Gizmos.color = Color.black;
Gizmos.DrawWireCube(attackPos.position, new Vector3(attackRangeX, attackRangeY, 1));
}
//Attack End
}
I don't use any rotation in my movement script.
Here is a screenshot of my scene with the range:
The most important thing right now is to get it to work, the visuals are second.
Also the AttackRangePos
is currently defined by an empty game object named "attackPos". (You can also see it in the screenshot I took). I think some people could get confused that the Sword is the attack position, but the Sword is only for visuals. Would still be essential for the sword to move too.
I also developed an idea, which could maybe deal with this problem:
The idea is basically that the attackPos gets the MoveValue, so it knows where the player is currently moving and then uses this information to move to the side of the player which is facing the direction of where the player is going. Since the attackPos is parented in the Player, the attackPos only needs to be moved by 1-2 units at max, so it shouldnt be to performance consuming, i hope.
I also want the attackPos to stay at its current position, even when you are not currently moving anymore. I would really apppreciate it, if you could help me fixing the problem that I have. To whoever this reads: Have a nice day!
@Maganta Hi, Maganta thnak you so much for you answer, I really appreciate it, but Im not sure if I used this infos right. Also I couldnt fit this in the comment section. My code is a total mess and Im kinda confused, especially because I dont get any errors, eventhough its not working. This ofc is because of me being unexpirienced and I have to admit I have no idea what I should do. Heres the code:
public class Move : MonoBehaviour
{
public int damage;
private float timeBtwAttack;
public float startTimeBtwAttack;
public float attackRange;
public Vector2 moveInputValue;
public Vector2 attackDir = Vector2.right; // independent direction of attack
public float attackCooldown;
float _lastAttackTime;
public Transform Player;
public Transform attackPos;
public LayerMask whatIsEnemies;
public float attackRangeX;
public float attackRangeY;
public float ValueIsGiven;
//Move Start
[SerializeField] private Rigidbody2D rb2D;
[SerializeField] private float speed;
private void OnMove(InputValue value)
{
var attackPos = (Vector2)transform.position + attackDir*attackRange; //Get the world coordinates of attackPos
moveInputValue = value.Get<Vector2>();
if(moveInputValue != Vector2.zero){
attackDir = moveInputValue.normalized; //When the player stops moving, use the last movement direction as the attack direction
}
if(ValueIsGiven >= 1){
Debug.Log(moveInputValue);
}
}
public float Angle(Vector2 vector2)
{
return 360 - (Mathf.Atan2(vector2.x, vector2.y) * Mathf.Rad2Deg * Mathf.Sign(vector2.x));
}
private void MoveLogicMethod()
{
// Vector2 result = moveInputValue * speed * Time.fixedDeltaTime;
Vector2 result = moveInputValue.normalized * speed; //fixedDeltaTime is not needed here
rb2D.velocity = result;
}
private void FixedUpdate()
{
MoveLogicMethod();
}
//Move End
//Attack Start
public void OnPlayerAttacking()
{
// If attacking too close to previous attack, ignore this input.
if (Time.time - _lastAttackTime < attackCooldown) return;
var enemiesToDamage = Physics2D.OverlapBoxAll(
attackPos.position,
new Vector2(attackRangeX, attackRangeY),
0,
whatIsEnemies
);
foreach (var enemy in enemiesToDamage) {
enemy.GetComponent<Enemy>().TakeDamage(damage);
}
_lastAttackTime = Time.time;
}
void OnDrawGizmosSelected(){
Gizmos.color = Color.black;
Gizmos.DrawWireCube(attackPos.position, new Vector2(attackRangeX, attackRangeY));
}
//Attack End
}
I also havent used any if statements yet, since I think it would even further confuse me. Im kind of ashamed asking you for help again, especially after you gave me such a long and good answer.