In this example, I start with the Background scene demo and add the ability to fire bullets.

While that demo doesn’t seem to fully work, the bullet firing part did work enough for me to understand.

Screenshot of phaser 3 Sprite with Shooting Bullets Example
Screenshot of phaser 3 Sprite with Shooting Bullets Example

Also, I attempted editing a good Nasa space image to use as a background. Because of the huge file size, I lowered the quality but I think I’ll keep looking for better images that look good and have low file size.

Using the Phaser 3 example Top-down Combat Mechanics as a start. I added some slight randomness to the bullet direction.

//https://www.phaser.io/examples/v3/view/games/topdownshooter/topdowncombatmechanics
class Bullet extends Phaser.GameObjects.Image {
    speed: number;
    born: number;
    direction: number;
    xSpeed: number;
    ySpeed: number;

    constructor(scene: any, x: number, y: number) {
        super(scene, x, y, 'bullet');
        this.speed = 1;
        this.born = 0;
        this.direction = 0;
        this.xSpeed = 0;
        this.ySpeed = 0;
        this.setSize(12, 12);

    }
    fire(shooter: Phaser.GameObjects.Sprite, target: { x: number, y: number }) {

        this.setPosition(shooter.x, shooter.y); // Initial position
        this.direction = Math.atan((target.x - this.x) / (target.y - this.y));
        //some light randomness to the bullet angle
        this.direction += ((Math.random() / 10) + (-(Math.random() / 10)));

        // Calculate X and y velocity of bullet to moves it from shooter to target
        if (target.y >= this.y) {
            this.xSpeed = this.speed * Math.sin(this.direction);
            this.ySpeed = this.speed * Math.cos(this.direction);
        }
        else {
            this.xSpeed = -this.speed * Math.sin(this.direction);
            this.ySpeed = -this.speed * Math.cos(this.direction);
        }

        this.rotation = shooter.rotation; // angle bullet with shooters rotation
        this.born = 0;
    }

    update(time: any, delta: number) {
        this.x += this.xSpeed * delta;
        this.y += this.ySpeed * delta;
        this.born += delta;

        if (this.born > 1500) {
            this.setActive(false);
            this.setVisible(false);
        }
    }
}

In the main scene, I added this to create –

this.playerBullets = this.physics.add.group({ classType: Bullet, maxSize: 50, runChildUpdate: true });

classType: Bullet – class name.

maxSize: 50 – the maximum number of bullets. I only set this to see how it works, 50 is fairly small and there will be pauses while firing waiting for fired bullets to recycle back into the available pool.

runChildUpdate: true – Since the bullet needs to update its position runChildUpdate must be true.

Full code of demo bullet scene

class BulletsScene extends Phaser.Scene {
private cursors: Phaser.Types.Input.Keyboard.CursorKeys;
private spaceship: Phaser.Physics.Arcade.Sprite;
private pointer: Phaser.Input.Pointer;
private touch: Phaser.Input.Pointer;
private playerBullets: any;
private shipSpeed: number;
constructor() {
super({
key: 'BulletsScene'
});
}
preload() {
this.load.image('ship', 'assets/sh2.png');
this.load.image('bullet', 'assets/bullet6.png');
//reduced quality and size of original from https://www.jpl.nasa.gov/images/spitzer/20110210/pia13845-full.jpg
this.load.image('piaback', 'assets/pia13845.png');
}
create() {
console.log('BulletsScene create');
this.shipSpeed = 400;
console.log(this.shipSpeed);
this.cameras.main.setBounds(0, 0, 4000, 4000);
this.physics.world.setBounds(0, 0, 4000, 4000);
this.add.image(0, 0, 'piaback').setOrigin(0).setScale(8);
this.spaceship = this.physics.add.sprite(this.game.scale.parentSize.width / 2, this.game.scale.parentSize.height / 2, 'ship');
this.spaceship.setDrag(35);//https://photonstorm.github.io/phaser3-docs/Phaser.Physics.Arcade.Components.Drag.html#setDrag
let menuItem: Phaser.GameObjects.Text = this.add.text(15, 15, "Home", { fontFamily: 'Verdana, "Times New Roman", Tahoma, serif', fontSize: 25, color: '#3333ff' });
menuItem.setInteractive({ cursor: 'pointer' }).on('pointerdown', () => { this.scene.start('MenuScene'); });
menuItem.setScrollFactor(0)
this.cursors = this.input.keyboard.createCursorKeys();
this.spaceship.setCollideWorldBounds(true);
this.pointer = this.input.activePointer;//https://photonstorm.github.io/phaser3-docs/Phaser.Input.Pointer.html
this.touch = this.input.pointer1;
this.cameras.main.startFollow(this.spaceship, true, 0.05, 0.05);
this.playerBullets = this.physics.add.group({ classType: Bullet, maxSize: 50, runChildUpdate: true });
this.input.keyboard.on('keydown_F', function () {
let bullet = this.playerBullets.get();
if (bullet) {
bullet.setActive(true).setVisible(true);
let velocity: Phaser.Math.Vector2 = new Phaser.Math.Vector2()
this.physics.velocityFromRotation(this.spaceship.rotation, this.shipSpeed, velocity);
bullet.fire(this.spaceship, { x: velocity.x * 180 / Math.PI, y: velocity.y * 180 / Math.PI });
}
}, this);
}
update() {
if (this.pointer.isDown) {
this.SetVelocityAndRotation(this.pointer);
} else if (this.touch.isDown) {
this.SetVelocityAndRotation(this.touch);
}
if (this.cursors.left.isDown) {
this.spaceship.angle -= 1;
}
if (this.cursors.right.isDown) {
this.spaceship.angle += 1
}
if (this.cursors.up.isDown) {
let velocity: Phaser.Math.Vector2 = new Phaser.Math.Vector2()
this.physics.velocityFromRotation(this.spaceship.rotation, this.shipSpeed, velocity);
this.spaceship.setVelocity(velocity.x, velocity.y);
}
if (this.cursors.down.isDown) {
this.spaceship.setVelocity(0);
}
}
SetVelocityAndRotation(pointer: Phaser.Input.Pointer) {
let velocity: Phaser.Math.Vector2 = new Phaser.Math.Vector2();
//when the world is larger than the screen we need worldX and worldY
let pointerWorld: Phaser.Geom.Point = new Phaser.Geom.Point(pointer.worldX, pointer.worldY);
this.spaceship.rotation = Phaser.Math.Angle.BetweenPoints(this.spaceship, pointerWorld);//https://photonstorm.github.io/phaser3-docs/Phaser.Math.Angle.html#.BetweenPoints__anchor
this.physics.velocityFromRotation(this.spaceship.rotation, this.shipSpeed, velocity);//https://photonstorm.github.io/phaser3-docs/Phaser.Physics.Arcade.ArcadePhysics.html#velocityFromRotation__anchor
this.spaceship.setVelocity(velocity.x, velocity.y);//https://photonstorm.github.io/phaser3-docs/Phaser.Physics.Arcade.Components.Velocity.html#setVelocity__anchor
let bullet = this.playerBullets.get();
if (bullet) {
bullet.setActive(true).setVisible(true);
let pointerWorld: Phaser.Geom.Point = new Phaser.Geom.Point(this.pointer.worldX, this.pointer.worldY);
bullet.fire(this.spaceship, pointerWorld);
}
}
}
//https://www.phaser.io/examples/v3/view/games/topdownshooter/topdowncombatmechanics
class Bullet extends Phaser.GameObjects.Image {
speed: number;
born: number;
direction: number;
xSpeed: number;
ySpeed: number;
constructor(scene: any, x: number, y: number) {
super(scene, x, y, 'bullet');
this.speed = 1;
this.born = 0;
this.direction = 0;
this.xSpeed = 0;
this.ySpeed = 0;
this.setSize(12, 12);
}
fire(shooter: Phaser.GameObjects.Sprite, target: { x: number, y: number }) {
this.setPosition(shooter.x, shooter.y); // Initial position
this.direction = Math.atan((target.x - this.x) / (target.y - this.y));
//some light randomness to the bullet angle
this.direction += ((Math.random() / 10) + (-(Math.random() / 10)));
// Calculate X and y velocity of bullet to moves it from shooter to target
if (target.y >= this.y) {
this.xSpeed = this.speed * Math.sin(this.direction);
this.ySpeed = this.speed * Math.cos(this.direction);
}
else {
this.xSpeed = -this.speed * Math.sin(this.direction);
this.ySpeed = -this.speed * Math.cos(this.direction);
}
this.rotation = shooter.rotation; // angle bullet with shooters rotation
this.born = 0;
}
update(time: any, delta: number) {
this.x += this.xSpeed * delta;
this.y += this.ySpeed * delta;
this.born += delta;
if (this.born > 1500) {
this.setActive(false);
this.setVisible(false);
}
}
}
export default BulletsScene

Currently, the bullet class is just defined right below the demo scene which works. However, it would be better to put this in its own file so it would be usable by any scenes that would need it.

In my next example, I will work on collision. This will require either adding an enemy or group of enemy sprites.

New Sprite Shooting Example added to Phaser 3 Examples Project

Leave a comment

Your email address will not be published. Required fields are marked *