[4.1] Projectiles in the platform module

dale_coop

Moderator
Staff member
I had a lot minutes scratching my head... what the hell I missed when setting my projectile game object. Why it doesn't move when my player shoots. After some tests, I realized that when objects are set as "ignore gravity"... it looks like they don't move (missing some update of Object_x_lo/hi variables).
To make it works, I wrote a small fix (that only will have impact on game objets and monsters that are set as "ignore gravity" and are meant to move).
Now, it should work :

Shoot.gif


So, here's my small tutorial :

How to add projectiles for your player, in the platformer game in NESMaker 4.1


1/ Open your platformer game project (scrolling or not scrolling) and go to "Project Settings > User Constants", select the "OBJECT_PLAYER_PROJECTILE" element and change its value to "02":

2019-01-06-16-41-51-Param-tres-du-projet.png

We will use the game object #$02 (the one named "Source Projectile").


2 / Set your "Source Projectile" object :

2019-01-06-16-42-20-NES-MAKER-4-1-0-Version-0x158-Mon-Jeu-Plateforme-MST.png


In the Details Object, set as "Player weapon", set also its Speed and Acceleration.

2019-01-06-20-52-19-Monster-Animation-Info.png


Then, in the "Actions", set the "Ignore Gravity".

2019-01-06-20-52-50-Monster-Animation-Info.png


Lastly, don't forget to give a Bounding Box.


3/ Make a new script "b_create_projectile_platformer.asm" in your "GameEngineData\Routines\Basic\ModuleScripts\InputScripts" folder, with that code:
Code:
	LDA gameHandler
	AND #%00100000	
	BEQ canShoot
	JMP doneShooting
canShoot:
	LDX player1_object
	;;; IF YOU WANT TO USE AN SPECIFIC ANIMATION / ACTION STEP WHEN SHOOTING, comment out the following 3 lines: 
	;;; check if already shooting (assuming the shoot action step is 05)
	;GetCurrentActionType player1_object
	;CMP #$05
	;BNE notAlreadyShooting
	JMP wasAlreadyShooting 
notAlreadyShooting
	;;; IF YOU WANT TO USE AN SPECIFIC ANIMATION / ACTION STEP WHEN SHOOTING, comment out the following 1 line: 
	;;; if not already shooting, change it's action step (assuming the shoot action step is 05)
	;ChangeObjectState #$05, #$02
	
	;;; if you want your player stops when he's shooting, comment out the following lines:
	;LDA Object_movement,x
	;AND #%00001111
	;STA Object_movement,x
	;LDA #$00
	;STA Object_h_speed_hi,x
	;STA Object_h_speed_lo,x
	;STA Object_v_speed_hi,x
	;STA Object_v_speed_lo,x
	
;; if was already shooting
wasAlreadyShooting:

	LDA Object_movement,x
	AND #%00000111
	STA temp2
	TAY

	LDA Object_x_hi,x
	SEC 
	SBC #$08 ;; width of projectile 
	STA temp
	LDA Object_scroll,x
	SBC #$00
	STA temp3

	LDA temp
	;;; offset x for creation
	CLC
	ADC projOffsetTableX,y
	STA temp
	LDA temp3
	ADC #$00
	STA temp3

	LDA Object_y_hi,x
	CLC
	ADC projOffsetTableY,y
	sec
	sbc #$08 ;; height of projectile
	STA temp1	
	
	
    CreateObject temp, temp1, #OBJECT_PLAYER_PROJECTILE, #$00, temp3
  
    ;;;; x is now the newly created object's x.
    LDA Object_movement,x
    ORA temp2
    STA Object_movement,x
    LDY temp2
    LDA directionTable,y
    ORA Object_movement,x
    STA Object_movement,x
	
    ;PlaySound #SND_SHOOT
doneShooting:
	RTS

Modify the "UserSubroutines.asm" script, located in your "GameEngineData\Routines\Basic\ModuleScripts" folder, adding this:
Code:
;; temporarly put this subroutine here, but would be better into a custom script.
;; fix for the non moving ignore-gravity-objects (like projectiles) in the platform games:
;; (used by the HandleHorizontalInertia macro)
updateXPosForNonPlayerObjects:
	CPX player1_object							;; check if it's the player
	BEQ doneUpdatingXPosForNonPlayerObjects		;; if it IS, don't need to update
	LDA Object_vulnerability,x
    AND #%00100000								;; check if "ignore gravity" is set
	BEQ doneUpdatingXPosForNonPlayerObjects		;; if it is NOT, don't need to update
	LDA Object_movement,x
	AND #%10000000 								;; check if movement horizontal engaged
	BEQ doneUpdatingXPosForNonPlayerObjects		;; if it is NOT, don't need to update
	LDA xHold_lo
	STA Object_x_lo,x
	LDA xHold_hi 
	STA Object_x_hi,x
	;LDA nt_hold
	;STA Object_scroll,x		;;not sure what to do with this
doneUpdatingXPosForNonPlayerObjects:
	RTS


4/ Modify the "HandleHorisontalInertia.asm" script (located in the "GameEngineData\Routines\Basic\System\Macros" folder).
At line 154, add those 2 lines :
Code:
	;; small fix for the ignore gravity objects (maybe issues with other objects in some another cases?)
	JSR updateXPosForNonPlayerObjects
	
	;;;; here is a macro that handles player guided right scrolling.
	;;;; you can disable right scrolling by simply commenting this one macro out.
Around line 199 (just after the "STA nt_hold" line), add those 2 lines :
Code:
	;; small fix for the ignore gravity objects (maybe issues with other objects in some another cases?)
	JSR updateXPosForNonPlayerObjects

	;;; this handles player guided left scrolling
	;;; to disable left scrolling, simply comment this one macro out.


6 / Modify (or preferred, duplicate the original one) your "TileCollisions.asm" script (in your "GameEngineData\Routines\Basic\ModuleScripts\MainScripts\ScrollingPlatformer" folder) , at line 126, add this line:
Code:
   CheckForHorizontalCollision  ;; <<--- checking solid collisions now
    
   ; JSR updateHorizontalPosition
   ; JSR updateVerticalPosition


Also, uncommenting the line 112:
Code:
	CheckPlayerCameraPosition

And also uncommenting the line 128:
Code:
	JSR updateHorizontalPosition


7/ Add the "b_create_projectile_platformer.asm" script to your "Scripts > Input Scripts", and assign it to the "Press" "B" button in your "Input Editor".

8/ Now, you can select the "Game Objects" element and click on the "Melee" button to select/display "Source" :

2019-01-06-16-42-39-NES-MAKER-4-1-0-Version-0x158-Mon-Jeu-Plateforme-MST.png

And set the position of your projectile when the player facing Right and Left.


9/ If you use Monster Barrier tile, you need also to modify the "MonsterBarrier.asm" script (in your "Routines\Basic\ModuleScripts\TileScripts\ScrollingPlatformer" folder), replacing with that code:

Code:
;;; SOLID
;;;This is how to inform a solid collision.
;;; You can also add this to the end of
;;; any tile type if you want it to have an effect AND
;;; be treated like solid.
;;; You could also check to see if it is a non-player object,
;;; and only return solid if it's a not player.  This would
;;; cause monsters to treat things like spikes or ladder or fire
;;; as solid while the player is able to interract with it.

	CPX player1_object
	BEQ skipSolidRead
	LDA Object_flags,x  ;; check the type of object
	AND #%00010100 		;; is it a player/monster weapon ?  
	BNE skipSolidRead	;; it IS, we skip	
	LDA #TILE_SOLID
	STA tile_solidity
skipSolidRead:	
	;; if you want it solid, declare it at the end


VoilĂ ! Now, projectiles should work.
 

ZeGGamer1

New member
I can create a projectile now, but I can only shoot on the ground, and if I shoot too many the game slows down. Is there a way to limit projectiles similar to the way used in 4.0.11, as well as a fix for the "Not being able to shoot in the air" issue?

PS) how did you get your health to work with the hearts?
 

ZeGGamer1

New member
OH, and the bullets don't destroy when hitting the ground despite me setting that in the object details menu
 

dale_coop

Moderator
Staff member
ZeGGamer1 said:
I can create a projectile now, but I can only shoot on the ground, and if I shoot too many the game slows down. Is there a way to limit projectiles similar to the way used in 4.0.11, as well as a fix for the "Not being able to shoot in the air" issue?

Just need a "limitProjectiles" variable... (to add, like the old tutorial for 4.0.X... If I find some time I will do an update to that previous tutorial)

ZeGGamer1 said:
PS) how did you get your health to work with the hearts?

In the "HUD & Boxes > HUD Elements", I set the "Element 2" as "0:var tiles" and set the "Empty Tile" and "Full Tile" (as heart).
In the "HUD & Boxes > User Variables", I set the "MyHealth" element initial value to "3" (because my player has 3 hearts).
And in my Player "Object Details", I set the value "3" for health.
The rest works automatically ;)
 

ZeGGamer1

New member
The HUD works! Thanks!

Let me Know when you make that update!
OH, and don't forget about the other problems, I'll see what I can do in the meantime
 

dale_coop

Moderator
Staff member
OK, I updated the script "b_create_projectile_platformer.asm" to be able to shoot while jumping.
(if you want to use a specific action step/animation for shooting... you need to comment out the 8,9,10 & 14 (and set your player's Action Step 05).
 
Thanks! This is a huge step towards getting the shooting working in my game again!

Also its looks like it will work better! Ignore gravity always screwed up my projectile in 4.0.11
 
I made an expansion to your shoot projectile script, but I am having a bit of trouble.

I made it where not only can you fire left and right, but also up, as well as the bullet offset adjusting for crouching while facing left or right.
It is not the prettiest, I couldn't figure out how to change the offset visually through the interface like your original code, but it works.
I only have trouble with one thing:
When you fire up, the bullet just hangs in the air. It doesn't actually fire up.

You have to add these user constants:
PLR_VERTICALSHOTOFFSET_Y -The vertical offset to start bullet from when aiming up.
PLR_VERTICALSHOTOFFSET_X_LEFT -The horizontal offset to start the bullet from when aiming up facing left.
PLR_VERTICALSHOTOFFSET_X_RIGHT -The horizontal offset to start the bullet from when aiming up facing right.
PLR_CROUCHSHOTOFFSET_Y -The vertical offset to start bullet from when crouching. The horizontal position is reused from shooting non-crouched.

modified input code:
Code:
LDA gameHandler
    AND #%00100000  
    BEQ canShoot
    JMP doneShooting
canShoot:
    LDX player1_object
    ;;; IF YOU WANT TO USE AN SPECIFIC ANIMATION / ACTION STEP WHEN SHOOTING, comment out the following 3 lines: 
    ;;; check if already shooting (assuming the shoot action step is 05)
    ;GetCurrentActionType player1_object
    ;CMP #$05
    ;BNE notAlreadyShooting
    JMP wasAlreadyShooting 
notAlreadyShooting
    ;;; IF YOU WANT TO USE AN SPECIFIC ANIMATION / ACTION STEP WHEN SHOOTING, comment out the following 1 line: 
    ;;; if not already shooting, change it's action step (assuming the shoot action step is 05)
    ;ChangeObjectState #$05, #$02
    
    ;;; if you want your player stops when he's shooting, comment out the following lines:
    ;LDA Object_movement,x
    ;AND #%00001111
    ;STA Object_movement,x
    ;LDA #$00
    ;STA Object_h_speed_hi,x
    ;STA Object_h_speed_lo,x
    ;STA Object_v_speed_hi,x
    ;STA Object_v_speed_lo,x
    
;; if was already shooting
wasAlreadyShooting:

    ;Store the player's movement data (facing direction?)
    LDA Object_movement,x
    AND #%00000111
    STA temp2
    TAY

    ;Store the width of the projectile in temp
    LDA Object_x_hi,x
    SEC 
    SBC #$08 ;; width of projectile 
    STA temp
    
    ;Store the players scrolling information
    LDA Object_scroll,x
    SBC #$00
    STA temp3

    ;Check to see if player is aiming up
    GetCurrentActionType player1_object
    CMP #04 ;; are we aiming up?
    BEQ wasAlreadyShooting_AimUP
    
    ;Check to see if player is crouched
    GetCurrentActionType player1_object
    CMP #03 ;; are we crouched?
    BEQ wasAlreadyShooting_AimHD
    
    ;Else fire normally
    JMP wasAlreadyShooting_AimHN
    
wasAlreadyShooting_AimUP:
    LDA Object_y_hi,x
    CLC
    ADC #PLR_VERTICALSHOTOFFSET_Y
    sec
    sbc #$08 ;; height of projectile
    STA temp1  

    LDA temp2
    CMP #%00000010 ;Facing Right
    BEQ wasAlreadyShooting_AimUP_Right
    JMP wasAlreadyShooting_AimUP_Left
    
wasAlreadyShooting_AimUP_Left:
    LDA temp
    ;;; offset x for creation
    CLC
    ADC #PLR_VERTICALSHOTOFFSET_X_LEFT
    STA temp
    LDA temp3
    ADC #$00
    STA temp3
    
    JMP wasAlreadyShooting_AimUP_SetShotUP
    
wasAlreadyShooting_AimUP_Right:
    LDA temp
    ;;; offset x for creation
    CLC
    ADC #PLR_VERTICALSHOTOFFSET_X_RIGHT
    STA temp
    LDA temp3
    ADC #$00
    STA temp3

    JMP wasAlreadyShooting_AimUP_SetShotUP
    
wasAlreadyShooting_AimUP_SetShotUP:
    ;Set the aimed direction to UP
    LDA #%00000100
    STA temp2
    TAY
    JMP wasAlreadyShooting_createProjectile
    
wasAlreadyShooting_AimHD:
    LDA temp
    ;;; offset x for creation
    CLC
    ADC projOffsetTableX,y
    STA temp
    LDA temp3
    ADC #$00
    STA temp3
    
    LDA Object_y_hi,x
    CLC
    ADC #PLR_CROUCHSHOTOFFSET_Y
    sec
    sbc #$08 ;; height of projectile
    STA temp1   
    
    JMP wasAlreadyShooting_createProjectile

wasAlreadyShooting_AimHN:
    LDA temp
    ;;; offset x for creation
    CLC
    ADC projOffsetTableX,y
    STA temp
    LDA temp3
    ADC #$00
    STA temp3

    LDA Object_y_hi,x
    CLC
    ADC projOffsetTableY,y
    sec
    sbc #$08 ;; height of projectile
    STA temp1   
    
wasAlreadyShooting_createProjectile:
    CreateObject temp, temp1, #OBJECT_PLAYER_PROJECTILE, #$00, temp3
  
    ;;;; x is now the newly created object's x.
    LDA Object_movement,x
    ORA temp2
    STA Object_movement,x
    LDY temp2
    LDA directionTable,y
    ORA Object_movement,x
    STA Object_movement,x
    
    PlaySound #SND_SHOOT
doneShooting:
    RTS
 
Say Quick Question I remember seeing a ammo counter where you cannot spam the gun or projectile while it is reloading and it will show up on the hud
Is that still good ?
 

dale_coop

Moderator
Staff member
Gilbertmaxter said:
Say Quick Question I remember seeing a ammo counter where you cannot spam the gun or projectile while it is reloading and it will show up on the hud
Is that still good ?

Sort of... I think you could reuse the same technique and make some modifications (like now you would just need to add a user variable, user constant instead of adding the variables directly in the script... then the inc and dec should remain... the power up script should be updated too.
But the theorical technique used should still be correct.

I could make un updated version for 4.1 if some people are interested.
 

dale_coop

Moderator
Staff member
chronicleroflegends said:
When you fire up, the bullet just hangs in the air. It doesn't actually fire up.

Yep, exactly like in the old 4.0.x version (no vertical movement for "ignore gravity" objects). So I didn't try to change that behavior in my fix.
But if you want, I could check if it's possible to get that... (I can't promise you I can fix it :p)
 
I am actually :)

I am however more interested in using it for a charger to were the laser recharges over time instead of a instant laser pickup i will still use it but i am looking into charging the laser on its own to prevent over shooting
 

dale_coop

Moderator
Staff member
Yeah, definilty would be possible... full charged lazer, when you shoot, it decreases... then increases (charging) automatically when you are not using it.
Will try to update my tutorial... (and add the automatic charge, as an option).
 
dale_coop said:
chronicleroflegends said:
When you fire up, the bullet just hangs in the air. It doesn't actually fire up.

Yep, exactly like in the old 4.0.x version (no vertical movement for "ignore gravity" objects). So I didn't try to change that behavior in my fix.
But if you want, I could check if it's possible to get that... (I can't promise you I can fix it :p)

I was curious if you may know a better solution.
In 4.0.11 I had a workaround in place:

When you create an object you can specify which state the object is in. So for the bullet I made 2 states:
Normal ignores gravity (and moves horizontally)
If you are aiming up though, it creates it in the second state, which is exactly the same but doesn't ignore gravity, so it will shoot up.
Have not yet tested to see if this still works in 4.1 (that is my project for today)
The only drawback of that is that the bullet is affected by gravity, so this requires the bullets move fast enough that it is guaranteed that they will hit a ceiling/screen edge no matter what. If not the bullet will fall back to the ground.
 
The workaround does still work.

You need to add one more constant:
PLR_VERTICALSHOTSPEED (I recommend 9 minimum)

You have to also make sure that state 00 of projectile ignores gravity and state 01 does not ignore gravity.

here is the modified code:
Code:
LDA gameHandler
    AND #%00100000  
    BEQ canShoot
    JMP doneShooting
canShoot:
    LDX player1_object
    ;;; IF YOU WANT TO USE AN SPECIFIC ANIMATION / ACTION STEP WHEN SHOOTING, comment out the following 3 lines: 
    ;;; check if already shooting (assuming the shoot action step is 05)
    ;GetCurrentActionType player1_object
    ;CMP #$05
    ;BNE notAlreadyShooting
    
    ;Check if we are at the projectile limit
    LDA projectileLimit
    BNE wasAlreadyShooting
    
    JMP doneShooting
    
notAlreadyShooting
    ;;; IF YOU WANT TO USE AN SPECIFIC ANIMATION / ACTION STEP WHEN SHOOTING, comment out the following 1 line: 
    ;;; if not already shooting, change it's action step (assuming the shoot action step is 05)
    ;ChangeObjectState #$05, #$02
    
    ;;; if you want your player stops when he's shooting, comment out the following lines:
    ;LDA Object_movement,x
    ;AND #%00001111
    ;STA Object_movement,x
    ;LDA #$00
    ;STA Object_h_speed_hi,x
    ;STA Object_h_speed_lo,x
    ;STA Object_v_speed_hi,x
    ;STA Object_v_speed_lo,x
    
;; if was already shooting
wasAlreadyShooting:

    ;Store the player's movement data (facing direction?)
    LDA Object_movement,x
    AND #%00000111
    STA temp2
    TAY

    ;Store the width of the projectile in temp
    LDA Object_x_hi,x
    SEC 
    SBC #$08 ;; width of projectile 
    STA temp
    
    ;Store the players scrolling information
    LDA Object_scroll,x
    SBC #$00
    STA temp3

    ;Check to see if player is aiming up
    GetCurrentActionType player1_object
    CMP #04 ;; are we aiming up?
    BEQ wasAlreadyShooting_AimUP
    
    ;Check to see if player is crouched
    GetCurrentActionType player1_object
    CMP #03 ;; are we crouched?
    BEQ wasAlreadyShooting_AimHD
    
    ;Else fire normally
    JMP wasAlreadyShooting_AimHN
    
wasAlreadyShooting_AimUP:
    LDA Object_y_hi,x
    CLC
    ADC #PLR_VERTICALSHOTOFFSET_Y
    sec
    sbc #$08 ;; height of projectile
    STA temp1  

    LDA temp2
    CMP #%00000010 ;Facing Right
    BEQ wasAlreadyShooting_AimUP_Right
    JMP wasAlreadyShooting_AimUP_Left
    
wasAlreadyShooting_AimUP_Left:
    LDA temp
    ;;; offset x for creation
    CLC
    ADC #PLR_VERTICALSHOTOFFSET_X_LEFT
    STA temp
    LDA temp3
    ADC #$00
    STA temp3
    
    JMP wasAlreadyShooting_AimUP_SetShotUP
    
wasAlreadyShooting_AimUP_Right:
    LDA temp
    ;;; offset x for creation
    CLC
    ADC #PLR_VERTICALSHOTOFFSET_X_RIGHT
    STA temp
    LDA temp3
    ADC #$00
    STA temp3

    JMP wasAlreadyShooting_AimUP_SetShotUP
    
wasAlreadyShooting_AimUP_SetShotUP:
    ;Set the aimed direction to UP
    LDA #%00000100
    STA temp2
    TAY
    JMP wasAlreadyShooting_createProjectileU
    
wasAlreadyShooting_AimHD:
    LDA temp
    ;;; offset x for creation
    CLC
    ADC projOffsetTableX,y
    STA temp
    LDA temp3
    ADC #$00
    STA temp3
    
    LDA Object_y_hi,x
    CLC
    ADC #PLR_CROUCHSHOTOFFSET_Y
    sec
    sbc #$08 ;; height of projectile
    STA temp1   
    
    JMP wasAlreadyShooting_createProjectileH

wasAlreadyShooting_AimHN:
    LDA temp
    ;;; offset x for creation
    CLC
    ADC projOffsetTableX,y
    STA temp
    LDA temp3
    ADC #$00
    STA temp3

    LDA Object_y_hi,x
    CLC
    ADC projOffsetTableY,y
    sec
    sbc #$08 ;; height of projectile
    STA temp1   
    
wasAlreadyShooting_createProjectileH:
    CreateObject temp, temp1, #OBJECT_PLAYER_PROJECTILE, #$00, temp3
    
    ;;;; x is now the newly created object's x.
    LDA Object_movement,x
    ORA temp2
    STA Object_movement,x
    LDY temp2
    LDA directionTable,y
    ORA Object_movement,x
    STA Object_movement,x
    
    JMP wasAlreadyShooting_createProjectileFinish
    
wasAlreadyShooting_createProjectileU:
    CreateObject temp, temp1, #OBJECT_PLAYER_PROJECTILE, #$01, temp3
    
    ;;;; x is now the newly created object's x.
    LDA Object_movement,x
    ORA temp2
    STA Object_movement,x
    LDY temp2
    LDA directionTable,y
    ORA Object_movement,x
    STA Object_movement,x
    
    LDA #$00
    SEC
    SBC #PLR_VERTICALSHOTSPEED
    STA Object_v_speed_hi,x

wasAlreadyShooting_createProjectileFinish: 
    ;Decrement the projectile limit
    DEC projectileLimit
   
    
    
    PlaySound #SND_SHOOT
doneShooting:
    RTS
 
Top Bottom