I am creating a game with JavaScript and the Phaser 3 framework. I have edited the hitbox of my target, but when an arrow hits, it just falls off screen. How do I make the arrow stick as it would in real life?
var physicsConfig = {
default: 'arcade',
arcade: {
debug: false //CHANGE THIS TO TRUE TO SEE LINES
}
}
var config = {
type: Phaser.AUTO,
width: 800,
height: 600,
physics: physicsConfig,
scene: {
preload: preload,
create: create,
update: update,
render: render
}
};
//Start the game
var game = new Phaser.Game(config);
function preload() {
this.load.image('sky', 'assets/sky.png');
this.load.spritesheet('archer', 'assets/archer_sprites.png', {
frameWidth: 128,
frameHeight: 128
});
this.load.image('target', 'assets/target.png');
this.load.image('ground', 'assets/ground.png');
this.load.image('rings', 'assets/rings.png');
this.load.image('arrow', 'assets/arrow.png');
this.load.audio('arrow_shot', 'assets/arrow_shooting.mp3');
}
function create() {
//Load all the images
this.add.image(400, 300, 'sky');
this.add.image(200, 200, 'ground');
this.add.image(300, 100, 'rings');
//Create the archer/player
this.player = this.physics.add.sprite(100, 410, 'archer');
this.player.setBounce(0.2);
this.player.setCollideWorldBounds(true);
//Shooting animation
this.anims.create({
key: 'shoot',
frames: this.anims.generateFrameNumbers('archer', {
start: 0,
end: 4
}),
frameRate: 20,
repeat: 0
});
//Create the target
this.target = this.physics.add.sprite(530, 365, 'target');
this.target.setSize(115, 95).setOffset(70, 130); //TARGET HITBOX
this.target.enableBody = true;
this.target.setImmovable();
//Create an array for arrows later
this.arrows = [];
//Get keypresses
this.cursors = this.input.keyboard.createCursorKeys();
//Assign input for spacebar
this.spacebar = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);
//Play sound when the arrow is shot
this.arrowSound = this.sound.add('arrow_shot');
}
function update() {
//Declare constants for movement
const playerMoveAmt = 200;
const arrowMoveAmt = 1750;
this.player.setDrag(2000);
//Rotation of the player
if (this.cursors.up.isDown && this.player.angle > -45) {
this.player.angle -= 1;
}
if (this.cursors.down.isDown && this.player.angle < 0) {
this.player.angle += 1;
}
//Shooting with the spacebar
if (Phaser.Input.Keyboard.JustDown(this.spacebar)) {
//Animate the shooting
this.player.anims.play('shoot', true);
//Arrow shooting
var arrow = this.physics.add.sprite(this.player.x, (this.player.y + 20), 'arrow');
arrow.enableBody = true;
arrow.body.immovable = false;
arrow.setGravityY(4000); //Gravity will affect the arrows
arrow.angle = this.player.angle //Angle the arrows with the player
arrow.setVelocityX(arrowMoveAmt);
arrow.setVelocityY((this.player.angle * 50));
this.arrows.push(arrow);
this.arrowSound.play();
}
}
function render() {}
body {
margin: 0;
}
<script src="//cdn.jsdelivr.net/npm/phaser@3.11.0/dist/phaser.js"></script>
I have tried doing this without the hitbox, and making the arrow's velocity 0 once it passed the target's x-value, but I need to do it properly, with collisions.
I have found a solution to accomplish the behavior that you want:
First, we'll check if the target sprite was touched from the left.
If the condition evaluates to true
, we'll set the arrow sprite gravity & velocity's values to 0;
You can use this code snippet (it starts after this.arrowSound.play()
):
function update () {
//Declare constants for movement
const playerMoveAmt = 200;
const arrowMoveAmt = 1500;
this.player.setDrag(2000);
//Move the player left or right
if (this.cursors.right.isDown)
this.player.setVelocityX(playerMoveAmt);
if (this.cursors.left.isDown)
this.player.setVelocityX(-playerMoveAmt);
//Rotation of the player
if (this.cursors.up.isDown && this.player.angle > -45) {
this.player.angle -= 1;}
if (this.cursors.down.isDown && this.player.angle < 0) {
this.player.angle += 1;}
//Shooting with the spacebar
if (Phaser.Input.Keyboard.JustDown(this.spacebar)) {
//Animate the shooting
this.player.anims.play('shoot', true);
//Arrow shooting
let arrow = this.physics.add.sprite(this.player.x, (this.player.y + 20), 'arrow');
arrow.enableBody = true;
arrow.body.immovable = false;
arrow.setGravityY(4000); //Gravity will affect the arrows
arrow.angle = this.player.angle //Angle the arrows with the player
arrow.setVelocityX(arrowMoveAmt);
arrow.setVelocityY((this.player.angle * 50));
this.arrows.push(arrow);
this.arrowSound.play();
} else if(this.target.body.touching.left) {
var i;
for (i = 0; i < this.arrows.length; i++) {
newArrows = this.arrows[i];
newArrows.setGravityY(0);
newArrows.setVelocityX(0);
newArrows.setVelocityY(0);
}
}
}
I would also suggest you to reduce the arrow sprite hitbox's size so it looks like it actually hits the target sprite. You can check the current hitbox's size by setting debug: true
like so:
var physicsConfig = {
default: 'arcade',
arcade: {
debug: true
}
}