I'm making a little game in java to learn, and I encounter a problem with my weapon hitbox, I tried a lot of things but the hitbox is never applied exactly where the weapon is.
The sword is correctly displayed, I tried to draw the hitbox but it didn't match with the real hitbox. Here is my code below:
I have a weapon class that will handle all other weapons in my game but for now I just have a sword class that is used. My problem is the interaction between monsters and weapon to apply damage to the monster. There is a problem with the hitbox that is not where the sword is displayed (which it is correctly displayed). When I try to drawRect the collision box, it does not appear. In reality I do not understand well how I can make my collision box that corresponds to the translation and rotation of the image of my sword.
package weapon; import entity.Entity; import main.GamePanel; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; public abstract class Weapon { protected GamePanel gp; public boolean isAttacking = false; public boolean collisionOn = false; //animations protected boolean isDrawing = false; protected BufferedImage[] frames; protected int currentFrame = 0; protected int counterFrames = 0; protected BufferedImage[] framesEffect; //angles public double angle = 0; protected double initialAngle = 0; protected double startAngle; protected double turn = Math.PI; //characteristics public int distanceFromPlayer = 50; protected int speed = 5; protected int damage; public Rectangle solidArea; public int solidAreaDefaultX, solidAreaDefaultY; public Weapon(GamePanel gp, int damage) { this.gp = gp; this.damage = damage; solidArea = new Rectangle(); solidArea.x = 0; solidArea.y = 0; solidAreaDefaultX = solidArea.x; solidAreaDefaultY = solidArea.y; solidArea.width = gp.tileSize - 2*solidArea.x; solidArea.height = gp.tileSize - solidArea.y; getImage(); getEffect(); } protected abstract void getImage(); protected abstract void getEffect(); public void startAttack(String direction) { if (!isDrawing) { isDrawing = true; startAngle = switch (direction) { case "up" -> Math.toRadians(180) + initialAngle; case "down" -> initialAngle; case "left" -> Math.toRadians(90) + initialAngle; case "right" -> Math.toRadians(-90) + initialAngle; default -> initialAngle; }; angle = startAngle; } } public void update() { if (isDrawing) { angle += Math.toRadians(speed); if (angle - startAngle >= turn) { isDrawing = false; } counterFrames++; if (counterFrames > 5) { currentFrame = (currentFrame + 1) % frames.length; counterFrames = 0; } int weaponHitboxX = gp.playerCenterX + (int) (distanceFromPlayer * Math.cos(angle) + gp.tileSize); int weaponHitboxY = gp.playerCenterY + (int) (distanceFromPlayer * Math.sin(angle)) + gp.tileSize / 2; solidArea.x = weaponHitboxX; solidArea.y = weaponHitboxY; int monsterIndex = checkEntityWithWeapon(this, gp.mon); if (monsterIndex != 999) { damageMonster(monsterIndex); } } } public void draw(Graphics2D g2, int playerCenterX, int playerCenterY) { if (isDrawing) { int weaponX = playerCenterX + (int) (distanceFromPlayer * Math.cos(angle) + gp.tileSize); int weaponY = playerCenterY + (int) (distanceFromPlayer * Math.sin(angle)) + gp.tileSize/2; AffineTransform originalTransform = g2.getTransform(); AffineTransform weaponTransform = new AffineTransform(); weaponTransform.translate(weaponX, weaponY); weaponTransform.rotate(angle + Math.PI / 2, gp.tileSize / 2, gp.tileSize / 2); g2.setTransform(weaponTransform); if (frames[currentFrame] != null) { g2.drawImage(frames[currentFrame], 0, 0, gp.tileSize, gp.tileSize, null);} g2.setTransform(originalTransform); } } public int checkEntityWithWeapon(Weapon weapon, Entity[] target) { int index = 999; for (int i = 0; i < target.length; i++) { if (target[i] != null) { target[i].solidArea.x = target[i].worldX + target[i].solidArea.x; target[i].solidArea.y = target[i].worldY + target[i].solidArea.y; if (weapon.solidArea.intersects(target[i].solidArea)) { // intersects vérifie si 2 rectangles sont en collision collisionOn = true; index = i; } target[i].solidArea.x = target[i].solidAreaDefaultX; target[i].solidArea.y = target[i].solidAreaDefaultY; } } return index; } public boolean isDrawing() { return isDrawing; } public int getWeaponTipY(int playerCenterY) { return playerCenterY + (int) (distanceFromPlayer * Math.sin(angle)); } public void damageMonster(int i){ if(i != 999) { if (gp.mon[i].invincible == false) { gp.mon[i].life -= 1; gp.mon[i].invincible = true; if(gp.mon[i].life <= 0){ gp.mon[i] = null; } } } } }
package weapon; import main.GamePanel; import main.UtilityTool; import java.awt.image.BufferedImage; public class Sword extends Weapon { public Sword(GamePanel gp) { super(gp, 10); solidArea.x = 24; solidArea.y = 0; solidAreaDefaultX = solidArea.x; solidAreaDefaultY = solidArea.y; solidArea.width = gp.tileSize - 2*solidArea.x; solidArea.height = gp.tileSize - solidArea.y; initialAngle = Math.toRadians(22.5); turn = Math.toRadians(135); } @Override protected void getImage() { BufferedImage swordSpriteSheet = UtilityTool.setup("/resources/weapons/sword2", 1, gp.tileSize); this.frames = UtilityTool.extractFrames(swordSpriteSheet, 1, gp.tileSize); } // ------------------------------not used for now------------------ protected void getEffect() { BufferedImage swordSpriteSheet = UtilityTool.setup("/resources/effects/slash1", 4, gp.tileSize); this.framesEffect = UtilityTool.extractFrames(swordSpriteSheet, 4, gp.tileSize); } }