PolyLine library - Marcus - 02-29-2024
Here's a small library that could be used for enemy movement in space shootemups (that's the only use I can think of), example included.
The truth is that I had made quite a nice shootemup that used the library. But when I was going to zip it I wanted to delete all built files (exe, n7a, n7b ...). Instead of deleting the exe file, I deleted the source code
Edit I updated the zip with a new version of the library and some more examples of how it can be used in a shootemup.
RE: PolyLine library - aliensoldier - 02-29-2024
I really like this library and I will use it when I make a shooting game.
You could make some more but simpler examples, to better understand how it is used.
RE: PolyLine library - kevin - 03-01-2024
(02-29-2024, 04:25 PM)Marcus Wrote: Here's a small library that could be used for enemy movement in space shootemups (that's the only use I can think of), example included.
Excellent - I too will be using this library.
I've blatently pinched your example to use as the basis for a quick editor so that I can amend the array in real time, to change the path that is generated.
Here's the code for the editor in case it may be of use to anyone. Instructions are on screen, and the saved text file includes the x and y points at the top, which could be read into any N7 file, and also a full array, which could be copied into your N7 file manually.
Code: ' polyline_example.n7
' -------------------
' The polyline library can be used for ... enemy paths in space shootemups, I guess :)
#win32
include "polyline.n7"
set window "Polyline", 1024, 768
set redraw off
visible points = []
'base array to start - can be modified if required
points = [[100,100],[100,400],[400,400],[400,100],[100,100]]
' Create a polyline from the points.
path = PolyLine(points)
distance = 0
curved = true
move_now = false
move_point = 999
while not keydown(KEY_ESCAPE, true)
if keydown(KEY_S) then save_file()
for i = 0 to sizeof(points) - 1
if mousebutton(0)
if mousex() > points[i][0] - 8 and mousex() < points[i][0] + 8
if mousey() > points[i][1] - 8 and mousey() < points[i][1] + 8
move_now = true
move_point = i
endif
endif
elseif mousebutton(1) and i < sizeof(points) - 1
if mousex() > (points[i + 1][0] - points[i ][0]) / 2 + points[i ][0]- 8 and mousex() < (points[i + 1][0] - points[i ][0]) / 2 + points[i ][0]+ 8
if mousey() > (points[i + 1][1] - points[i][1]) / 2 + points[i ][1]- 8 and mousey() < (points[i + 1][1] - points[i][1]) / 2 + points[i ][1]+ 8
points = insert_into_array(points,i + 1,[mousex(),mousey()])
endif
endif
endif
next
if move_now = true
points[move_point][0] = mousex()
points[move_point][1] = mousey()
path = PolyLine(points)
if not mousebutton(0) then move_now = false
endif
' Toggle curved mode.
if keydown(KEY_SPACE, true) curved = not curved
' Move forward 4 pixels, wrap at length of path.
distance = (distance + 4)%path.GetLength()
' 'GetPoint' returns the position along the path at specified travel distance. If the second
' parameter is true, the point is computed for a composite bezier curve that passes through all
' the original points. If false, a position along the polyline is returned. If distance is
' outside the length of the polyline, unset is returned.
' Note that the function always returns the same array (but with different values, of course).
pos = path.GetPoint(distance, curved)
set color 0, 0, 0 , 24
cls
' Draw lines.
set color 255,255,255 ' 64, 64, 64
for i = 0 to sizeof(points) - 2
draw line points[i][0], points[i][1], points[i + 1][0], points[i + 1][1]
draw rect points[i][0] - 8, points[i][1] - 8,16,16
'set color 255,0,0
if i < sizeof(points) - 1
draw rect (points[i + 1][0] - points[i ][0]) / 2 + points[i ][0]- 8,
(points[i + 1][1] - points[i][1]) / 2 + points[i ][1]- 8,16,16,1
endif
set color 255,255,255
next
draw rect points[sizeof(points) - 1][0] - 8, points[sizeof(points) - 1][1] - 8,16,16
' Draw points.
set color 96, 96, 96
for i = 0 to sizeof(points) - 1 draw ellipse points[i][0], points[i][1], 2, 2, true
' Draw circle that moves along path.
set color 255, 255, 255
draw ellipse pos[0], pos[1], 5, 5, true
set caret width(primary)/2, height(primary) - 250
' Instructions.
set caret width(primary)/2, height(primary) - fheight()*12
center "Press and hold the left mouse button in a transparent rectangle to move the points." ;wln
center "Press and release the right mouse button in a solid rectangle to add a new point." ;wln
center "Press the S key to save to a text file";wln
center "Press space to toggle curve mode"
redraw
fwait 60
wend
###################################################################################################
function save_file()
newfile = savefiledialog("txt")
if instr(newfile, ".txt") < 0
newfile = newfile + ".txt"
endif
f = createfile(newfile)
if file(f)
write file f, sizeof(points)
wln file f
for i = 0 to sizeof(points) - 1
write file f , int(points[i][0]) + " "
write file f , int(points[i][1]) + " "
wln file f
next
wln file f ' now write out the full array, ready to be copy and pasted into a N7 file
write file f, "["
for i = 0 to sizeof(points) - 1
write file f ,"["
write file f , int(points[i][0]) + ","
if i < sizeof(points) - 1
write file f , int(points[i][1]) + "],"
else
write file f , int(points[i][1]) + "]"
endif
next
write file f, "]"
free file f
endif
endfunc
#########################################################################################
function insert_into_array(array,element,new_element)
temp = copy(array)
temp[sizeof(temp)] = new_element
temp2 = []
for i = 0 to element - 1
temp2[sizeof(temp2)] = temp[i]
next
temp2[element] = temp[sizeof(temp) - 1]
for i = element to sizeof(temp) - 1
temp2[sizeof(temp2)] = temp[i]
next
free key temp2,sizeof(temp2)-1
array = copy(temp2)
return array
endfunc
###########################################
RE: PolyLine library - Marcus - 03-01-2024
Can't check the code right now, typing on my phone. Something like this:
Code: filename = savefiledialog("txt")
if filename
if instr(filename, ".txt") < 0 filename = filename + ".txt"
f = createfile(filename)
if file(f)
' write all the stuff.
...
free file f
endif
endif
RE: PolyLine library - kevin - 03-01-2024
Works great thanks Marcus - I have amended the code in my post above.....
RE: PolyLine library - Marcus - 03-01-2024
Nice and simple editor, I'll use it when I write examples on how to use the library for enemy waves!
There's actually a built in command to insert stuff into an existing array. Nope, for some reason it's not documented, and F1 in NED shows no help. But here we go:
Code: points = insert_into_array(points,i + 1,[mousex(),mousey()])
can be done with:
Code:
insert points, i + 1, [mousex(), mousey()]
RE: PolyLine library - kevin - 03-01-2024
(03-01-2024, 06:32 PM)Marcus Wrote: There's actually a built in command to insert stuff into an existing array. Nope, for some reason it's not documented, and F1 in NED shows no help. But here we go:
Code: points = insert_into_array(points,i + 1,[mousex(),mousey()])
can be done with:
Code:
insert points, i + 1, [mousex(), mousey()]
That's good to know - I thought that it would have been a good command to have, and spent a while going through the documentation. No harm done though - it was a good challenge to write a function for it
When you are considering examples to write for the library, would you consider this, if it is possible with the library? I have code below which is the basis for a space invader type game. Every 2 seconds, an invader is chosen at random, and its state is changed from "active_now" to "diving". What I have been trying to code is for the chosen invader to proceed along the Polyline for a full cycle, and then return to where he would have been in the original invader grid. It would then revert from the state of "diving" back to "active_now". Hope this is clear? Thanks.
Code: include "polyline.n7"
visible screen_w = 640,screen_h = 480
'Open a window
set window "Invaders",screen_w,screen_h
'enable double buffering
set redraw off
'==== set up the invaders ========
visible number_of_invaders = 24
visible invaders = fill([x:0,y:0,w:32,h:16,img:0,x_inc:1,y_inc:20,active_now:true,diving:false],number_of_invaders)
startx = 100
starty = 50
countx = 0
for i = 0 to number_of_invaders - 1
invaders[i].x = startx
invaders[i].y = starty
startx = startx + 40
countx = countx + 1
if countx = 8
countx = 0
startx = 100
starty = starty + 24
endif
next
'========================================
visible spaceship = [x:screen_w / 2,y:screen_h - 80,w:64,h:32]
'========================================
visible spaceship_missiles = []
visible max_spaceship_missiles = 12
'======== polyline stuff ====================
visible points = [[100,100],[100,400],[400,400],[400,100],[100,100]]
' Create a polyline from the points.
visible path = PolyLine(points)
distance = 0
curved = false
speed = 4
timer = 0
############ to calculate FPS ##########################
visible framecount,lasttime = 999,fps,frametime = 0,starttime = 0,endtime = 0
##########################################################
##############################################################################################
####################################### GAME LOOP #########################################
##############################################################################################
do
### for FPS calc #########
framecount = framecount + 1
starttime = clock()
###########################
timer = (timer + 1 ) % 120
if timer = 119 then launch_invader()
move_invaders()
move_spaceship()
'clear the screen
set color 255,255,255
cls
set color 0,0,0
for i = 0 to number_of_invaders - 1
if invaders[i].active_now or invaders[i].diving
if invaders[i].diving = true
draw rect invaders[i].x,invaders[i].y,invaders[i].w,invaders[i].h,false
else
draw rect invaders[i].x,invaders[i].y,invaders[i].w,invaders[i].h,true
endif
endif
next
set color 255,0,0
draw rect spaceship.x,spaceship.y,spaceship.w,spaceship.h
set color 0,0,0
set caret screen_w/2,10
write "mouse = " + mousex() + " / " + mousey();wln
write "FPS = " + str(fps);wln
write sizeof(path)
'copy back buffer to the screen
redraw
'cap FPS to 60
fwait 60
####### FPS calc ############################
endtime = clock()
frametime = frametime + endtime - starttime
if frametime > 1000 # 1 second
fps = framecount
framecount = 0
frametime = 0
endif
################################################
until keydown(KEY_ESCAPE)
#########################################################################################
################################# FUNCTIONS ###########################################
#########################################################################################
function move_invaders()
for i = 0 to number_of_invaders - 1
if invaders[i].active_now or invaders[i].diving
invaders[i].x = invaders[i].x + invaders[i].x_inc
if invaders[i].x > screen_w - invaders[i].w * 2 or invaders[i].x < invaders[i].w
for j = 0 to number_of_invaders - 1
invaders[j].x_inc = - invaders[j].x_inc
invaders[j].y = invaders[j].y + invaders[j].y_inc
next
endif
endif
next
endfunc
################################################
function move_spaceship()
if keydown(KEY_LEFT) then spaceship.x = max(spaceship.x - 2,0)
if keydown(KEY_RIGHT) then spaceship.x = min(spaceship.x + 2,screen_w - spaceship.w)
endfunc
################################################
function launch_invader()
possible_diver = []
selected_diver = 9999
for p = 0 to number_of_invaders - 1
if invaders[p].active_now
possible_diver[sizeof(possible_diver)] = p
endif
next
selected_diver = rnd(sizeof(possible_diver))
invaders[selected_diver].diving = true
invaders[selected_diver].active_now = false
endfunc
################################################
RE: PolyLine library - johnno56 - 03-01-2024
Marcus,
I wonder if the Bezier Curve Library, from N6, could be used to create "paths" for diving invaders?
Oh... just remembered... the Cubic Bezier Curve routine, from N6's Blastemroids, might work as well?...
Just a thought..
RE: PolyLine library - Marcus - 03-01-2024
(03-01-2024, 09:36 PM)johnno56 Wrote: Marcus,
I wonder if the Bezier Curve Library, from N6, could be used to create "paths" for diving invaders?
Oh... just remembered... the Cubic Bezier Curve routine, from N6's Blastemroids, might work as well?...
Just a thought..
Definitely, I've used regular bezier curves (quadratic and cubic) for enemy waves and dives in the past.
The special thing with polyline.n7 is that it creates composite bezier curves (many bezier curves patched together smoothly) that passes through all the points that you supply. So the enemy paths can be pretty wicked.
RE: PolyLine library - Marcus - 03-02-2024
(03-01-2024, 08:05 PM)kevin Wrote: (03-01-2024, 06:32 PM)Marcus Wrote: There's actually a built in command to insert stuff into an existing array. Nope, for some reason it's not documented, and F1 in NED shows no help. But here we go:
Code: points = insert_into_array(points,i + 1,[mousex(),mousey()])
can be done with:
Code:
insert points, i + 1, [mousex(), mousey()]
That's good to know - I thought that it would have been a good command to have, and spent a while going through the documentation. No harm done though - it was a good challenge to write a function for it
When you are considering examples to write for the library, would you consider this, if it is possible with the library? I have code below which is the basis for a space invader type game. Every 2 seconds, an invader is chosen at random, and its state is changed from "active_now" to "diving". What I have been trying to code is for the chosen invader to proceed along the Polyline for a full cycle, and then return to where he would have been in the original invader grid. It would then revert from the state of "diving" back to "active_now". Hope this is clear? Thanks.
Code: include "polyline.n7"
visible screen_w = 640,screen_h = 480
'Open a window
set window "Invaders",screen_w,screen_h
'enable double buffering
set redraw off
'==== set up the invaders ========
visible number_of_invaders = 24
visible invaders = fill([x:0,y:0,w:32,h:16,img:0,x_inc:1,y_inc:20,active_now:true,diving:false],number_of_invaders)
startx = 100
starty = 50
countx = 0
for i = 0 to number_of_invaders - 1
invaders[i].x = startx
invaders[i].y = starty
startx = startx + 40
countx = countx + 1
if countx = 8
countx = 0
startx = 100
starty = starty + 24
endif
next
'========================================
visible spaceship = [x:screen_w / 2,y:screen_h - 80,w:64,h:32]
'========================================
visible spaceship_missiles = []
visible max_spaceship_missiles = 12
'======== polyline stuff ====================
visible points = [[100,100],[100,400],[400,400],[400,100],[100,100]]
' Create a polyline from the points.
visible path = PolyLine(points)
distance = 0
curved = false
speed = 4
timer = 0
############ to calculate FPS ##########################
visible framecount,lasttime = 999,fps,frametime = 0,starttime = 0,endtime = 0
##########################################################
##############################################################################################
####################################### GAME LOOP #########################################
##############################################################################################
do
### for FPS calc #########
framecount = framecount + 1
starttime = clock()
###########################
timer = (timer + 1 ) % 120
if timer = 119 then launch_invader()
move_invaders()
move_spaceship()
'clear the screen
set color 255,255,255
cls
set color 0,0,0
for i = 0 to number_of_invaders - 1
if invaders[i].active_now or invaders[i].diving
if invaders[i].diving = true
draw rect invaders[i].x,invaders[i].y,invaders[i].w,invaders[i].h,false
else
draw rect invaders[i].x,invaders[i].y,invaders[i].w,invaders[i].h,true
endif
endif
next
set color 255,0,0
draw rect spaceship.x,spaceship.y,spaceship.w,spaceship.h
set color 0,0,0
set caret screen_w/2,10
write "mouse = " + mousex() + " / " + mousey();wln
write "FPS = " + str(fps);wln
write sizeof(path)
'copy back buffer to the screen
redraw
'cap FPS to 60
fwait 60
####### FPS calc ############################
endtime = clock()
frametime = frametime + endtime - starttime
if frametime > 1000 # 1 second
fps = framecount
framecount = 0
frametime = 0
endif
################################################
until keydown(KEY_ESCAPE)
#########################################################################################
################################# FUNCTIONS ###########################################
#########################################################################################
function move_invaders()
for i = 0 to number_of_invaders - 1
if invaders[i].active_now or invaders[i].diving
invaders[i].x = invaders[i].x + invaders[i].x_inc
if invaders[i].x > screen_w - invaders[i].w * 2 or invaders[i].x < invaders[i].w
for j = 0 to number_of_invaders - 1
invaders[j].x_inc = - invaders[j].x_inc
invaders[j].y = invaders[j].y + invaders[j].y_inc
next
endif
endif
next
endfunc
################################################
function move_spaceship()
if keydown(KEY_LEFT) then spaceship.x = max(spaceship.x - 2,0)
if keydown(KEY_RIGHT) then spaceship.x = min(spaceship.x + 2,screen_w - spaceship.w)
endfunc
################################################
function launch_invader()
possible_diver = []
selected_diver = 9999
for p = 0 to number_of_invaders - 1
if invaders[p].active_now
possible_diver[sizeof(possible_diver)] = p
endif
next
selected_diver = rnd(sizeof(possible_diver))
invaders[selected_diver].diving = true
invaders[selected_diver].active_now = false
endfunc
################################################
I've added some functions to a polyline so that you can access and modify its control points. The new polyline.n7 is attached to this post. I also took the liberty to add code for diving enemies to your game. I've marked all changes with "Marcus"
Code: include "polyline.n7"
visible screen_w = 640,screen_h = 480
'Open a window
set window "Invaders",screen_w,screen_h
'enable double buffering
set redraw off
'==== set up the invaders ========
visible number_of_invaders = 24
visible invaders = fill([x:0,y:0,w:32,h:16,img:0,x_inc:1,y_inc:20,active_now:true,diving:false],number_of_invaders)
startx = 100
starty = 50
countx = 0
for i = 0 to number_of_invaders - 1
invaders[i].x = startx
invaders[i].y = starty
startx = startx + 40
countx = countx + 1
if countx = 8
countx = 0
startx = 100
starty = starty + 24
endif
next
'========================================
visible spaceship = [x:screen_w / 2,y:screen_h - 80,w:64,h:32]
'========================================
visible spaceship_missiles = []
visible max_spaceship_missiles = 12
'======== polyline stuff ====================
visible points = [[100,100],[100,400],[400,400],[400,100],[100,100]]
' Create a polyline from the points.
visible path = PolyLine(points)
distance = 0
curved = false
speed = 4
timer = 0
############ to calculate FPS ##########################
visible framecount,lasttime = 999,fps,frametime = 0,starttime = 0,endtime = 0
##########################################################
##############################################################################################
####################################### GAME LOOP #########################################
##############################################################################################
do
### for FPS calc #########
framecount = framecount + 1
starttime = clock()
###########################
timer = (timer + 1 ) % 120
if timer = 119 then launch_invader()
move_invaders()
move_spaceship()
'clear the screen
set color 255,255,255
cls
set color 0,0,0
for i = 0 to number_of_invaders - 1
if invaders[i].active_now or invaders[i].diving
if invaders[i].diving = true
draw rect invaders[i].x,invaders[i].y,invaders[i].w,invaders[i].h,false
' Marcus
draw rect invaders[i].dive_x, invaders[i].dive_y, invaders[i].w, invaders[i].h, true
' EndMarcus
else
draw rect invaders[i].x,invaders[i].y,invaders[i].w,invaders[i].h,true
endif
endif
next
set color 255,0,0
draw rect spaceship.x,spaceship.y,spaceship.w,spaceship.h
set color 0,0,0
set caret screen_w/2,10
write "mouse = " + mousex() + " / " + mousey();wln
write "FPS = " + str(fps);wln
write sizeof(path)
'copy back buffer to the screen
redraw
'cap FPS to 60
fwait 60
####### FPS calc ############################
endtime = clock()
frametime = frametime + endtime - starttime
if frametime > 1000 # 1 second
fps = framecount
framecount = 0
frametime = 0
endif
################################################
until keydown(KEY_ESCAPE)
#########################################################################################
################################# FUNCTIONS ###########################################
#########################################################################################
function move_invaders()
for i = 0 to number_of_invaders - 1
if invaders[i].active_now or invaders[i].diving
invaders[i].x = invaders[i].x + invaders[i].x_inc
if invaders[i].x > screen_w - invaders[i].w * 2 or invaders[i].x < invaders[i].w
for j = 0 to number_of_invaders - 1
invaders[j].x_inc = - invaders[j].x_inc
invaders[j].y = invaders[j].y + invaders[j].y_inc
next
endif
endif
' Marcus
if invaders[i].diving
' Set the last control point of the path to the invaders normal position.
' A problem here is that the regular invaders move vertically with huge steps. This
' doesn't look good for the movement of a diver. So rather than doing this:
'invaders[i].path.ModifyControlPoint(2, invaders[i].x, invaders[i].y)
' , we can interpolate the y coordinate:
y = invaders[i].path.GetControlPoint(2)[1] ' current y of the last control point.
invaders[i].path.ModifyControlPoint(2, invaders[i].x, y*0.95 + invaders[i].y*0.05)
' Move 4 pixels.
invaders[i].path_dist = invaders[i].path_dist + 4
pos = invaders[i].path.GetPoint(invaders[i].path_dist, true)
' Still on path?
if pos
invaders[i].dive_x = pos[0]
invaders[i].dive_y = pos[1]
' No longer diving.
else
invaders[i].active_now = true
invaders[i].diving = false
endif
endif
' EndMarcus
next
endfunc
################################################
function move_spaceship()
if keydown(KEY_LEFT) then spaceship.x = max(spaceship.x - 2,0)
if keydown(KEY_RIGHT) then spaceship.x = min(spaceship.x + 2,screen_w - spaceship.w)
endfunc
################################################
function launch_invader()
possible_diver = []
selected_diver = 9999
for p = 0 to number_of_invaders - 1
if invaders[p].active_now
possible_diver[sizeof(possible_diver)] = p
endif
next
selected_diver = rnd(sizeof(possible_diver))
invaders[selected_diver].diving = true
' Marcus
i = invaders[selected_diver]
' Very simple path, just dive to the bottom center of the screen and back again.
i.path = PolyLine([[i.x, i.y], [width(primary)/2, height(primary) - 20], [i.x, i.y]])
' Set traveled distance to 0.
i.path_dist = 0
' We still need to update the diver's regular position. So when diving, the invader's position is
' stored in dive_x, dive_y. Use these coordinates when drawing and doing collision tests for a
' diving invader.
i.dive_x = i.x
i.dive_y = i.y
' EndMarcus
invaders[selected_diver].active_now = false
endfunc
################################################
Don't hesitate to ask if you find something weird
|