[SOLVED] Ray Cast issue on a FPS project

Hello. I’m trying to implement the ray casting system in my FPS game for the shooting mechanic. I previously used rigidbody and realized that it’s not an ideal system for shooting bullets in a FPS game. So after some research and asking around, this is the current state of my script:

var Gun = pc.createScript('gun');
Gun.attributes.add('maxAmmo', { type: 'number' });
Gun.attributes.add('magazineSize', { type: 'number', default: 30 });
Gun.attributes.add('bullet', { type: 'entity' });
Gun.attributes.add('gunpower', { type: 'number', default: 1 });
Gun.attributes.add('bulletCount', { type: 'entity' });
Gun.attributes.add('currentAmmo', { type: 'number' });
Gun.attributes.add('reloadTime', { type: 'number', default: 2 });
Gun.attributes.add('reloadText', { type: 'entity' });
Gun.attributes.add('cameraEntity', { type: 'entity' });

// initialize code called once per entity
Gun.prototype.initialize = function () {
    this.currentAmmo = this.maxAmmo;
    this.isReloading = false;
    //this.setMouse();
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.setMouse, this);

};

// update code called every frame
Gun.prototype.update = function (dt) {
    this.setKeyboard();
    this.bulletCount.element.text = this.currentAmmo + '/' + this.magazineSize;
};

Gun.prototype.ammoAmount = function (currentWeaponAmmo, currentWeaponMaxAmmo) {
    this.currentAmmo = currentWeaponAmmo;
    this.maxAmmo = currentWeaponMaxAmmo;
};


Gun.prototype.setMouse = function (event) {
    var app = this.app;

    app.mouse.on("mousedown", function () {
        app.mouse.enablePointerLock();
        if (this.app.mouse.isPressed(pc.MOUSEBUTTON_LEFT)) {
            if (this.entity.enabled) {
                this.shoot();
            }
        }
    }, this);
};

Gun.prototype.setKeyboard = function (dt) {
    var app = this.app;

    if (app.keyboard.isPressed(pc.KEY_R)) {
        this.Reload();
    }

    if (this.currentAmmo == 0) {
        this.reloadText.element.text = 'Reload';
    }
    else {
        this.reloadText.element.text = null;
    }
};

Gun.prototype.shoot = function (screenPosition) {
    this.isReloading = false;
    if (this.currentAmmo == 0) {
        console.log("out of ammo");
        return false;
    }

    var from = this.cameraEntity.getPosition();
    var to = this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip);
    var result = this.app.systems.rigidbody.raycastFirst(from, to);






    // // Clone the bullet as specified by the attribute
    // var bullet = this.bullet.clone();
    // // Add it to the game 
    // this.app.root.addChild(bullet);

    // var player = this.entity;
    // var target = this.entity.findByName('target');
    // // Its force is in the direction the player is facing 
    // this.force = new pc.Vec3();
    // this.force.copy(target.forward);
    // this.force.copy(target.forward);
    // this.force.scale(this.gunpower);
    // console.log(target);

    // var pos = target.getPosition();
    // var direction = target.forward;
    // // Add it a little further if the player is already going fast
    // // pos.add(direction.scale(5 + gun.rigidbody.linearVelocity.length() * 0.01));

    // bullet.setPosition(pos);
    // bullet.setPosition(pos);
    // // var bulletRotation =  player.getRotation();
    // // bullet.setRotation( bulletRotation );
    // // bullet.rotateLocal(90,0,0);

    // bullet.enabled = true; //Must enable after setting position!

    // bullet.rigidbody.applyImpulse(this.force);



    this.currentAmmo--;
    this.bulletCount.element.text = this.currentAmmo + '/' + this.magazineSize;

};

Gun.prototype.Reload = function () {
    //
    if (this.currentAmmo == this.maxAmmo) {
        return false;
    }
    //

    isReloading = true;
    this.currentAmmo = this.maxAmmo;
    this.bulletCount.element.text = this.currentAmmo + '/' + this.magazineSize;

    /*if (this.magazineSize >= this.maxAmmo) {
        this.currentAmmo = this.maxAmmo;
        this.magazineSize -= this.maxAmmo;
    } else {
        this.currentAmmo = this.magazineSize;
        this.magazineSize = 0;
    }
    this.isReloading = false;*/
};

When i launch the game, i get this error message: " Uncaught TypeError: Cannot read properties of undefined (reading ‘x’)"

I learned that this issue has something to do with the mouse event but i couldn’t pinpoint what it exactly is despite my research. I checked couple of examples of ray casting on PC but each of them uses a different method so i’m confused about what to do. I would appreciate any help for this. Thanks in advance

@Ronin This is the typical procedure I use in all my scripts for shooting.

// initialize code called once per entity
GunShoot.prototype.initialize = function() {

    // Event for mouse button left fire
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.mouseDown, this);

};
// Handle mouse down event

GunShoot.prototype.mouseDown = function(e) {

    // Check for Mouse left button

    if(this.entity.enabled && e.button == pc.MOUSEBUTTON_LEFT) {

        // Process action

        this.shoot();            

    }

};
// handle shooting and raycast
GunShoot.prototype.shoot = function(e) {

    // Get center of screen
    var centerScreenX = this.app.graphicsDevice.width / 2;
    var centerScreenY = this.app.graphicsDevice.height / 2;

    //Get start and end point
    var start = this.camera.camera.screenToWorld(centerScreenX, centerScreenY, this.camera.camera.nearClip);
    var end = this.camera.camera.screenToWorld(centerScreenX, centerScreenY, this.camera.camera.farClip);
    
    // raycast between the two points and return the closest hit result
    var result = this.app.systems.rigidbody.raycastFirst(start, end);

    // Play and reset muzzle flash
    this.muzzleFx.particlesystem.play();
    this.muzzleFx.particlesystem.reset();

    if(this.entity.sound != null) {
        // Play this weapons sound
        this.entity.sound.play("fire");
    }

    // We hit something with collision
    if(result) {

        console.log("You Hit: " + result.entity.name);
        this.handleImpact(result.entity,result.point,result.normal);
 
    } else {

        console.log("Nothing Hit");
    }

};

I am not sure what is going on with the snippet above. You are calling setMouse to enablePointerLock and then after the entity.enable you fire shoot. Is there a reason for this method that I am missing? The reason for your error is probably not getting the screen center “.x” before using the screenToWorld(); The shoot function never passes this info. it’s empty. Please have a look to this.

“You are calling setMouse to enablePointerLock” setMouse is the function which handles everything about the mouse actions that’s why enablePointerLock is there. “then after the entity.enable you fire shoot.” this part is complicated to explain, you need to see the project to understand. It’s a solution to decrement the ammo amount of each weapon seperately when they shoot. After i posted this topic, i made this change in the initialize function:

this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.shoot, this);

and i stopped getting any errors but i don’t know if it’s working either. Also after that change i can only move the camera when i hold the left mouse button. Any idea why?

@Ronin Are you now getting your to/from from inside the shoot function? You must have made some changes. Is there a link somewhere to your project?

“Are you now getting your to/from from inside the shoot function?” it was there before, you must’ve overlooked that part:

var from = this.cameraEntity.getPosition();
    var to = this.cameraEntity.camera.screenToWorld(screenPosition.x, screenPosition.y, this.cameraEntity.camera.farClip);
    var result = this.app.systems.rigidbody.raycastFirst(from, to);

https://playcanvas.com/editor/scene/1528185

@Ronin If it was why didn’t you pass it in the shoot function? I’ll take a look.

i did it the same way you do. only difference is i don’t have this in my shoot script

var centerScreenX = this.app.graphicsDevice.width / 2;
    var centerScreenY = this.app.graphicsDevice.height / 2;

i looked at different examples, in non of them they defined the centerScreenX and centerScreenY. Maybe i need to do it. But i want to test it if it works first and fix the mouse movement

ok now i get why mouse movement is broken, it’s bcs of this line:

this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.shoot, this);

but if i don’t add it, i get the " Uncaught TypeError: Cannot read properties of undefined (reading ‘x’)"

@Ronin You still have the app.mouse.enablePointerLock(); In this setMouse function. Nothing will happen until you do this. It’s on your left mouse click. Plus, the error is still present.

i changed the setMouse function:

Gun.prototype.setMouse = function (event) {
    var app = this.app;

    if (this.app.mouse.isPressed(pc.MOUSEBUTTON_LEFT)) {
        if (this.entity.enabled) {
            this.shoot();
        }
    }

};

and call it in the init. Mouse movement is still broken

If you want to sent the event to a second function too, then you need to add the event when you call that function.

this.shoot(event);

If you want to call the event ‘event’, then you need to use ‘event’.

event.x
event.y
1 Like

after if do this

this.shoot(event);

i stopped getting errors and mouse look works fine now. Do i also need to change the screenPosition.x with the the event.x ?

Great!

Yes, exactly.

1 Like

do i have to define them first?

You already do that here:

this.shoot(event);

ok i tested it, ray cast works!! But it doesn’t shoot where i aim at, not even close. Can’t hit anything other than the ground right now :smile:

@Ronin Your bullet entity does not seem like it is attached to the gun which is attached to the player.

no, it is attached, also bullet object is not needed in ray casting. i don’t understand why you said that

Probably because you use pointerLock? Then there is no visible mouse to shoot with?

@Ronin The bullet has to move with the weapon correct?
image