NaaLaa
PolyLine library - Printable Version

+- NaaLaa (https://www.naalaa.com/forum)
+-- Forum: NaaLaa (https://www.naalaa.com/forum/forum-1.html)
+--- Forum: NaaLaa 7 Code (https://www.naalaa.com/forum/forum-4.html)
+--- Thread: PolyLine library (/thread-90.html)

Pages: 1 2 3


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 Sad


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 Smile
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 Smile
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" Smile

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 Smile