Post new topic    
Slime
Send private message
Bullet script help 
 PostThu Nov 12, 2020 8:54 pm
Send private message Reply with quote
Fenders Sky0003.gif
Hi,

Iv'e been struggling on with this script for the last few days and it has me stuck.

I am trying to make an area with a mini game.
One character (NPC) will fire a projectile and your character has to fire a projectile and hit it as it goes past you.

I have figured out through trial and error, the plotscripting dictionary and helpful things on this forum how to fire the bullets.
However I am struggling with the collision part.
As you can see from the gif the created bullet just flies though the other one with no interaction.
Could someone please take a look at my script below and see if they can figure out where I am going wrong.

Thanks in advance!


plotscript,targetpractice,begin

suspend player
suspend caterpillar
fade screen out
pathfind hero to (0, 9, 12)
pathfind hero to (1, 10, 11)
pathfind hero to (2, 10, 12)
pathfind hero to (3, 11, 12)
Wait for hero (1)
wait (3)
set hero direction (0,North)
set hero direction (1,North)
set hero direction (2,North)
set hero direction (3,North)
wait (1)
pan camera(north,2)
set tag (152,on)
set tag (153,on)
set hero picture(0,2,outside battle)
set hero picture(1,3,outside battle)
wait (1)
Fade screen in
resume player


while (true) do, begin

wait (1)

if(keypress (key:space)) then (shoot)
else (checkhit)

end
end

plotscript, shoot, begin


variable (x,y,d)
x:=hero X(me)
y:=hero Y(me)
d:=hero direction(me)

if (d==north) then(decrement(y))
if (d==south) then(increment(y))
if (d==west) then(decrement(x))
if (d==east) then(increment(x))

create NPC(3,x,y,d)
set NPC obstructs (3, false)
set NPC ignores walls (3, true)
walk NPC (3,d,7)
wait for npc (3)
destroy npc (3)

end

plotscript, checkhit, begin

variable (Blast, goop)
variable (BlastX, BlastY)
Variable (goopX, goopY)

Blast:=NPC reference (3,)
goop:=NPC reference (1,)

BlastX:=NPC X(Blast)
BlastY:=NPC Y(Blast)

goopX:=NPC X(goop)
goopY:=NPC Y(goop)


if (BlastX == goopX && BlastY == goopY) then
(destroy NPC (1)
destroy NPC (3)
exit script)

end
Liquid Metal King Slime
Send private message
 
 PostFri Nov 13, 2020 2:08 am
Send private message Reply with quote
This command disables NPC collision for the bullet:

Code:
set NPC obstructs (3, false)


EDIT: but actually, that is okay. The REAL problem is this line

Code:
if (BlastX == goopX && BlastY == goopY) then


You should not expect them to be on the exact same x/y pixel, that will never happen (or almost never)

Instead you should check that the difference between the x values of the two bullets is smaller than the sum of their speed (and the same for the y)

EDIT2: oh! Another issue!

Code:
create NPC(3,x,y,d)
set NPC obstructs (3, false)
set NPC ignores walls (3, true)
walk NPC (3,d,7)
wait for npc (3)
destroy npc (3)


So you create the shot, make it move, wait for it, then destroy it. This all happens while all other scripts are frozen. Only one script at a time can execute.

Remove the "wait for npc" and the "destroy npc" and instead you will have to add a check in the main loop to check if the movement has finished.

I would write an example if I wasn't typing on a phone right now. If you are still stuck I can help more on another day
Slime
Send private message
 
 PostFri Nov 13, 2020 9:58 pm
Send private message Reply with quote
Thanks for the quick response.

How do I check the difference between the X values of the two bullets is smaller than the sum of their speed?

I tried the below which did very little to remedy the problem. So I assume this isn't what you meant.

if ((get NPC speed (3)+get NPC speed (1)) < BlastX -- goopX && (get NPC speed (3)+get NPC speed (1)) < BlastY -- goopY) then (destroy npc (1)) else (wait for npc (3), (destroy npc(3)) exit script)

instead of

if (BlastX == goopX && BlastY == goopY) then (destroy NPC (1) destroy NPC (3) exit script).

Although on test it did work once but iv'e not managed to replicate that again.
Metal King Slime
Send private message
 
 PostMon Nov 16, 2020 2:25 am
Send private message Reply with quote
I see you were using "NPC X/Y", which return the tile, not "NPC pixel X/Y". You need to ensure you don't mix pixels and tiles.

Because you were checking tiles, if you had just removed the lines "wait for npc (3), destroy npc (3)" which James pointed out, you would have seen the script work most of the time but not reliably, because often the bullets would be on the same tile at the same time.

Likewise you shouldn't have "else (wait for npc (3) ..." in the collision check, because then if they're not colliding on the very first frame, the script will wait and do nothing.

If the NPC speeds sum to less than 20, then they can't pass through each other within a single frame, so it would be sufficient to simply check whether the NPCs overlap. You can do that with
Code:
if (slice collide (get npc slice(1), get npc slice(3)))

However, since they're bullets you might want to give them both a speed of 10 or more.

More generally, you have to do it similar to how James said. Even more generally, you would also add in the size of the NPCs to check for collision, if you don't then you're just checking whether the NPCs are passing each other (which is OK when the NPCs are just moving along a straight line towards each other).

The tricky bit is that the correct check depends on both movement speed and direction of movement.
Here is a very general script for NPC collision checking. (In fact, it's fact more reliable than the engine's builtin checking.) You're not expected to understand this, but it could be useful for other people.
I want to add builtin commands to do collision checking like this.

Code:

# Get the X velocity of an NPC, in pixels
script, get npc move x, npc, begin
  if (npc is walking (npc) == false) then (exit returning (0))
  switch (npc direction (npc)) do (
    case(left)  return (-1 * get npc speed(npc))
    case(right) return (get npc speed(npc))
  )
end

# Get the Y velocity of an NPC, in pixels
script, get npc move y, npc, begin
  if (npc is walking (npc) == false) then (exit returning (0))
  switch (npc direction (npc)) do (
    case(up)   return (-1 * get npc speed(npc))
    case(down) return (get npc speed (npc))
  )
end

# Does collision checking for a single axis (X or Y) of two moving boxes.
# Returns true if two intervals [x1, x1+w1) and [x2, x2+w2) intersect or will intersect
# when shifting by (velocities) v1 and v2.
script, axis collide, x1, v1, w1, x2, v2, w2, begin
  # Shift frame of reference so v1 is stationary
  v2 -= v1

  # Check whether x2 moved from left of x1+w1 to right of x1
  if (x2 < x1 + w1 && x2 + v2 + w2 > x1) then (return (true))
  # Check whether x2 moved from right of x1 to left of x1+w1
  if (x2 + w2 > x1 && x2 + v2 < x1 + w1) then (return (true))
end

# Return true if two NPCs, each possibly moving, are intersecting or about to collide
# (will either intersect or pass through each other)
script, npcs will collide, npc1, npc2, begin
  variable (x1, y1, x2, y2, v1x, v1y, v2x, v2y)
  x1 := npc pixel x (npc1)
  y1 := npc pixel y (npc1)
  v1x := get npc move x (npc1)
  v1y := get npc move y (npc1)
  x2 := npc pixel x (npc2)
  y2 := npc pixel y (npc2)
  v2x := get npc move x (npc2)
  v2y := get npc move y (npc2)

  return (axis collide(x1, v1x, 20, x2, v2x, 20) && axis collide(y1, v1y, 20, y2, v2y, 20))
end


And then your checkhit script becomes:

Code:

plotscript, checkhit, begin
  variable (Blast, goop)
  Blast := global NPC reference (0)
  goop := NPC reference (0)
  if (Blast == false || goop == false) then (exit script)

  # This is a good place to clean up bullets
  if (npc is walking(Blast) == false) then (
    destroy npc (Blast)
    exit script
  )

  if (npcs will collide(Blast, goop)) then (
    destroy NPC (Blast)
    destroy NPC (goop)
  )
end


There is one problem with this, which is that actually it returns true the frame before they will actually collide, perhaps happening too early.

I found another problem while testing. Write
Code:
if(keypress (key:space)) then (shoot)
checkhit

Not
Code:
if(keypress (key:space)) then (shoot)
else (checkhit)


Otherwise it can miss collision if you're standing next to the target and fire.




BTW, don't write "NPC reference (3,)". I'm surprised that HSpeak allows that extra comma. A future version likely won't.

You might also like to write "keypress (use key)" instead of "keypress (key:space)". Using "use key" gives you gamepad support.
Slime
Send private message
 
 PostMon Nov 16, 2020 7:25 pm
Send private message Reply with quote
Thanks for the help.

I did remove the "wait for npc (3), destroy npc (3)" which is probably why It started working on very rare occasions.
Iv'e read through the script and if I am honest it's a bit mind blowing, although I think I understand about 60-70% of it.

Enough of it to get it working at least.

thanks for your tips its all very helpful.
Usually I don't script anything beyond some tile based picture puzzles but I think bullets was possibly quite a large step beyond what I am capable of at the minute.

A built in command to check for collisions would be fantastic and would be a nice thing to see in the future.

thanks again for your help!
Display posts from previous: