Menu

Show posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Show posts Menu

Messages - kanonet

#1096
Noch ein Bugreport (hat vermutlich nichts mit V9 zu tun):
Wenn mit INIOPEN eine Datei geöffnet werden soll in der ein Wert auf ä,ö,ü oder ß endet, dann stürzt das Programm sofort mit Fehlermeldung ab. Hänge ich an den Wert aber noch z.B. ein Leerzeichen an, gibt es keine Probleme und alles läuft und wird geladen, wie es soll.
#1097
I created my own simple "key"-management, for user defined keys. A "key" can be a key from keyboard, mousemovement, mousebutton, joystickmovement or joystickbutton, you dont have to care about what the player use, the key-management will do all on its own. Keyboardlayouts are stored in .ini-files, i created one for us/uk and one for german keyboard, but of cause you can convert/create others for other countries (please share them with us/me!).

Its really simple to use:
1. create a new key:
Code (glbasic) Select
LOCAL attack AS Tkey

2. let the player define his key:
Code (glbasic) Select
REPEAT
PRINT "Define control for attack",0,0
SHOWSCREEN
UNTIL attack.SET()
or autoset it (usefull if you load keysettings from a ini-file):
Code (glbasic) Select
attack.SET(1, 57)and you can add a secondary/alternativ key:
Code (glbasic) Select
attack.SET(2) or attack.SET(2, 28)

3. in your mainloop, check whether the player use the key:
Code (glbasic) Select
IF attack.DOWN() THEN DOSOMETHING()This checks primary and secondary key.


TKey now informs you about keyup/-down event.:
//  0 = not pressed / nicht gedrückt
//  2 = just pressed / gerade runtergedrückt
//  1 = pressed / gedrückt (gehalten)
// -1 = release event / wieder losgelassen
Of cause it only works, if you use DOWN() in your loop...


The ini-files for us and german keyboard are attached, here is the code for the key-management:
Code (glbasic) Select
// --------------------------------- //
// Project: MyKey
// Autor: kanonet
// Start: Tuesday, February 08, 2011
// Last Update: December 6, 2012

GLOBAL KEY_mouse_sensitivity% = 2 // sensitivity of the mouse
GLOBAL KEY_joystick_sensitivity# = 0.35 // sensitivity of the joystick
GLOBAL KEY_keymap$[]
GLOBAL KEY_joystick_count% // for intern use, do not change
GLOBAL KEY_ini_file$ = "keymap_us.ini" // which INI file to read for keyboard layout


//! Type of a "key". It is possible to set a primary and a secondary "key".
//! "key" can be a key of the keyboard, a mousemovement, a mousebutton, a joystickmovement or a joystickbutton.
//! After a "key" was set with SET(), you can use DOWN() to check whether the player use it.
//? Typ eines "keys". Es ist möglich einen primären und einen sekundären "key" zu festzulegen.
//? "key" kann eine Taste der Tastatur, eine Mausbewegung, ein Mausbutton, eine Joystickbewegung oder ein Joystickbutton sein.
//? Nachdem ein "key" mit SET() gestzt wurde, kann mit DOWN() geprüft werden, ob er benutzt wird.
TYPE Tkey
code% // eventcode of the "key" if "key" is on the keyboard, code = scancode of the key
name$
code2%
name2$
state%

//! Initialise/sets the "key". Can get used as often as its needed, until played sets a "key".
//! You can change the "key" as often, as you want. You can define a alternativ "key" also.
//? Initialisiert/setzt den "key". Kann beliebig oft aufgerufen werden, bis der Spieler einen "key" gesetzt hat.
//? Ein gesetzter "key" kann beliebig oft geändert werden. Es kann außerdem auch ein alternativer "key" definiert werden.
// \param num% - Optional, use function call to set the primary (num%=1) or secondary (num%=2) "key". | Optional, mit funltionsaufruf wird der primäre (num%=1) oder der sekundäre (num%=2) "key" festgelegt.
// \param code% - Optional, only use this if you want to select a "key", not the player!  | code% - Optional, sollte nur genutz werden, wenn man selbst ein "key" setzen und dies nicht den Spieler überlassen will!
// \return 0 if no "key" was set, otherwise code% of the "key". | 0 wenn kein "key" gesetzt wurde, ansonsten code% des gesetzten "keys".
FUNCTION SET: num%=1, code%=0

LOCAL event%, name$
event=code

IF NOT code // only check for userinput if no key was specified

// check Keyboard
FOR i=1 TO 237
IF i=89 THEN i=153
IF KEY(i)
event=i; BREAK
ENDIF
NEXT


IF NOT event

// check mouse
IF MOUSEAXIS(3); event=331
ELSEIF MOUSEAXIS(4); event=332
ELSEIF MOUSEAXIS(5); event=333
ELSEIF MOUSEAXIS(0)<-KEY_mouse_sensitivity; event=301
ELSEIF MOUSEAXIS(0)>KEY_mouse_sensitivity; event=302
ELSEIF MOUSEAXIS(1)<-KEY_mouse_sensitivity; event=311
ELSEIF MOUSEAXIS(1)>KEY_mouse_sensitivity; event=312
ELSEIF MOUSEAXIS(2)>0; event=321
ELSEIF MOUSEAXIS(2)<0; event=322
ELSE
// check joystick
KEY_joystick_count%=GETNUMJOYSTICKS()-1
FOR i=0 TO KEY_joystick_count
IF GETJOYX(i)<-KEY_joystick_sensitivity; event=401+i*100; BREAK
ELSEIF GETJOYX(i)>KEY_joystick_sensitivity; event=402+i*100; BREAK
ELSEIF GETJOYY(i)<-KEY_joystick_sensitivity; event=411+i*100; BREAK
ELSEIF GETJOYY(i)>KEY_joystick_sensitivity; event=412+i*100; BREAK
ELSEIF GETJOYZ(i)<-KEY_joystick_sensitivity; event=421+i*100; BREAK
ELSEIF GETJOYZ(i)>KEY_joystick_sensitivity; event=422+i*100; BREAK
ELSEIF GETJOYRX(i)<-KEY_joystick_sensitivity; event=403+i*100; BREAK
ELSEIF GETJOYRX(i)>KEY_joystick_sensitivity; event=404+i*100; BREAK
ELSEIF GETJOYRY(i)<-KEY_joystick_sensitivity; event=413+i*100; BREAK
ELSEIF GETJOYRY(i)>KEY_joystick_sensitivity; event=414+i*100; BREAK
ELSEIF GETJOYRZ(i)<-KEY_joystick_sensitivity; event=423+i*100; BREAK
ELSEIF GETJOYRZ(i)>KEY_joystick_sensitivity; event=424+i*100; BREAK
ELSEIF GETDIGIX(i)<-KEY_joystick_sensitivity; event=431+i*100; BREAK
ELSEIF GETDIGIX(i)>KEY_joystick_sensitivity; event=432+i*100; BREAK
ELSEIF GETDIGIY(i)<-KEY_joystick_sensitivity; event=441+i*100; BREAK
ELSEIF GETDIGIY(i)>KEY_joystick_sensitivity; event=442+i*100; BREAK
ELSE
FOR j=0 TO 31
IF GETJOYBUTTON(i, j); event=451+j+i*100; BREAK; ENDIF
NEXT
ENDIF
NEXT
ENDIF

ENDIF

ENDIF

// DIM array for the keyboardlayout
IF KEY_keymap_Str.count <> 324
DIM KEY_keymap$[324]
LOCAL j=0
INIOPEN KEY_ini_file$

FOR i%=0 TO 452
IF i=238
i=301
ELSEIF i=334
i=400
ENDIF
KEY_keymap$[j]=INIGET$("keymap", i, "")
INC j
NEXT
INIOPEN ""
ENDIF

// give readable names
SELECT event
CASE 0 TO 237; name$=KEY_keymap$[event]
CASE 301 TO 333; name$=KEY_keymap$[event-63]
CASE 401 TO (KEY_joystick_count*100+482)
code=MOD(event,100)
name$=GETJOYNAME$(event/100-4)
IF name$="" THEN name$=KEY_keymap$[271]+FORMAT$(2,0,event/100-3)
SELECT code
CASE 1 TO 42; name$=name$+" "+KEY_keymap$[code+271]
CASE 51 TO 82; name$=name$+" "+KEY_keymap$[322]+(code-50)
ENDSELECT
DEFAULT; name$=KEY_keymap$[323]
ENDSELECT

// write the key on specified slot
IF num=2
IF event=self.code
event=0
name$=KEY_keymap$[0]
ENDIF
self.code2%=event
self.name2$=name$
ELSE
IF event=self.code2
event=0
name$=KEY_keymap$[0]
ENDIF
self.code%=event
self.name$=name$
ENDIF
RETURN event

ENDFUNCTION

//! Detect whether a "key" is used by the player or not.
//? Ermittelt ob ein "key" vom Spieler genutzt wird.
// \return 0 if "key" is not in use, otherwise >0. (if "key" is mouse or joystick movementspeed will be returned)  | 0 wenn "key" nicht genutzt wird, ansonsten >0. (ist "key" Maus oder Joystick, wird die Bewegungsgeschwindigleit zurück gegeben)
// \return TKey.state% will tell you, if key was used before: 0=not pressed, 2=just pressed, 1=pressed, -1=release event. | TKey.state% gibt zurück, ob key genutzt wurde: 0=nicht gedrückt, 2=gerade runtergedrückt, 1=gedrückt (gehalten), -1=wieder losgelassen.
FUNCTION DOWN:

STATIC joy, event%, KEY_joystick_sensitivity=0.2
FOR i=0 TO 1
IF i=0
event=self.code
ELSE
event=self.code2
ENDIF

SELECT event
CASE 0; joy=0
CASE 1 TO 237 // check keyboard
joy=KEY(event)
CASE 301 TO 350 // check mouse
SELECT event
CASE 301; joy=MOUSEAXIS(0)/20.; IF joy<0; joy=-joy; ELSE; joy=0; ENDIF
CASE 302; joy=MOUSEAXIS(0)/20.; IF joy<0 THEN joy=0
CASE 311; joy=MOUSEAXIS(1)/20.; IF joy<0; joy=-joy; ELSE; joy=0; ENDIF
CASE 312; joy=MOUSEAXIS(1)/20.; IF joy<0 THEN joy=0
CASE 321; joy=MOUSEAXIS(2)>0
CASE 322; joy=MOUSEAXIS(2)<0
CASE 331; joy=MOUSEAXIS(3)
CASE 332; joy=MOUSEAXIS(4)
CASE 333; joy=MOUSEAXIS(5)
ENDSELECT
CASE 401 TO (KEY_joystick_count*100+482) // check joytick
joy=MOD(event,100)
event=event/100-4
SELECT joy
CASE 1; joy=GETJOYX(event); IF joy<-KEY_joystick_sensitivity; joy=-joy; ELSE; joy=0; ENDIF
CASE 2; joy=GETJOYX(event); IF joy<KEY_joystick_sensitivity THEN joy=0
CASE 11; joy=GETJOYY(event); IF joy<-KEY_joystick_sensitivity; joy=-joy; ELSE; joy=0; ENDIF
CASE 12; joy=GETJOYY(event); IF joy<KEY_joystick_sensitivity THEN joy=0
CASE 21; joy=GETJOYZ(event); IF joy<-KEY_joystick_sensitivity; joy=-joy; ELSE; joy=0; ENDIF
CASE 22; joy=GETJOYZ(event); IF joy<KEY_joystick_sensitivity THEN joy=0
CASE 3; joy=GETJOYRX(event); IF joy<-KEY_joystick_sensitivity; joy=-joy; ELSE; joy=0; ENDIF
CASE 4; joy=GETJOYRX(event); IF joy<KEY_joystick_sensitivity THEN joy=0
CASE 13; joy=GETJOYRY(event); IF joy<-KEY_joystick_sensitivity; joy=-joy; ELSE; joy=0; ENDIF
CASE 14; joy=GETJOYRY(event); IF joy<KEY_joystick_sensitivity THEN joy=0
CASE 23; joy=GETJOYRZ(event); IF joy<-KEY_joystick_sensitivity; joy=-joy; ELSE; joy=0; ENDIF
CASE 24; joy=GETJOYRZ(event); IF joy<KEY_joystick_sensitivity THEN joy=0
CASE 31; joy=GETDIGIX(event); IF joy<-KEY_joystick_sensitivity; joy=-joy; ELSE; joy=0; ENDIF
CASE 32; joy=GETDIGIX(event); IF joy<KEY_joystick_sensitivity THEN joy=0
CASE 41; joy=GETDIGIY(event); IF joy<-KEY_joystick_sensitivity; joy=-joy; ELSE; joy=0; ENDIF
CASE 42; joy=GETDIGIY(event); IF joy<KEY_joystick_sensitivity THEN joy=0
CASE 51 TO 82; joy=SGN(GETJOYBUTTON(event, joy-51))
DEFAULT; joy=0
ENDSELECT
DEFAULT; joy=0
ENDSELECT
IF joy THEN BREAK
NEXT
// check if key was used before
IF joy
IF self.state
self.state=1
ELSE
self.state=2
ENDIF
ELSE
IF self.state>0
self.state=-1
ELSE
self.state=0
ENDIF
ENDIF
RETURN joy

ENDFUNCTION

ENDTYPE

Please let me know what you think about this and report bugs. I dont have a real joystick/gamepad (i just used a virtual one) and my keyboard has no numpad/mediakeys, so it would be really nice if someone could test this for me. EDIT: Testing done by Ragaril, thank you!
EDIT2: small Update.
EDIT3: small Update. :P
EDIT4: there was a small typo in last Update. :-[

[attachment deleted by admin]
#1098
Bei mir scheint strg+c nicht zu funktionieren.
Außerdem kann ich ein kompiliertest und gestartetes Programm nicht mehr "nach vorne holen", solange kein showscreen ausgeführt wird (keine Ahnung, ob das bei V8 auch war).
#1099
Ahh, ok. The problem do not appear now.
#1100
"The requested URL /beta/SEUCK.rar was not found on this server."
So i can't test...
#1101
I played around with FINDPATH() a bit, 'cuz i created my own: http://www.glbasic.com/forum/index.php?topic=5450.msg43705
My one is slower (except very short paths) so this wouldn't help you, but what i learned about Kitty's FINDPATH() is:
Debugmode slows it down very hard! So disable debug, if you need speed. And of cause, smaller maps/paths can speed up your search.;)
#1102
Ok, i use this old thread.;)

I downloaded today and found this bug:


[attachment deleted by admin]
#1103
Hehe, na dann danke ich für die Blumen. :enc:
#1104
Ich bin mir nicht ganz sicher, ob ich die Intention deiner Frage richtig verstanden habe.

Möchtest du darauf hinweisen, dass sie falsche Wege gefunden hat? Wenn ja, dann bitte ich darum, dass du mir einen Screenshot oder ähnliches zukommen lässt, damit ich die Situation rekonstruieren und den Fehler suchen und beheben kann.

Ansonsten, falls es wirklich nur eine Frage ist:
Kurze Antwort: ja könnte wohl funktionieren,  jedoch wurde sie nicht dafür geschaffen/optimiert.
Lange Antwort: theoretisch könnte sie dies vermutlich leisten, jedoch würde ich sie dafür umschreiben.  Die hier gepostete Form ist auf Geschwindigkeit optimiert, sie soll nach Möglichkeit zur Laufzeit eingesetzt werden können; soll das errechnen eines (oder zur Not auch mehrerer) Wege ermöglichen, ohne das Programm/Spiel ins stocken zu bringen. Für ein Navi würde ich auf einen langsameren, aber noch exakteren Algorithmus zurückgreifen (mit nur einem Suchlauf aus nur einer Richtung), schließlich wäre es da nicht schlimm, wenn die Berechnung einige Sekunden dauern würde, diese ist da ja das einzige, was man berechnet... Für ein Navi würde man aber vermutlich keine Karte (Raster) verwende wie ich es hier tat (für die meisten Spiele sollte das in Ordnung sein, ist auch einfach zu handhaben), sondern man würde wohl eher auf eine Form neuronales Netz zurückgreifen (die wenigsten Straßennetze sind ausschließlich rechtwinklig ;)), wobei die Routine natürlich auch daran (ohne zu großem Aufwand) angepasst werden könnte. Letztendlich entscheidet ohnehin das Kartenmaterial alles...
#1105
Quote from: Kitty Hello on 2010-Dec-21
FINDPATH geht auch 8 Richtungen. Deine Funktion kann aber viel besser angepasst werden. Ist gut!
Könntest du das bitte näher erläutern/mit Beispiel belegen? In der Hilfe befindet sich nur ein Beispiel für die 4-Wege-Suche. Aber du hast natürlich Recht, größter Vorteil der Routine ist es, dass man sie anpassen kann, wie man es braucht (ist auch nicht schwer sich auf 4 Richtungen zu beschränken ;)), ich dachte da Beispielsweise an die Verwendung mehrerer Karten (eine für die geografischen Beschaffenheiten, eine für den Territorialbesitz, eine für besonders gefährliche Gegenden,...) in einer Suche, wäre einfach und schnell.

Quote from: Ocean on 2010-Dec-21Danke Kanonet für diese Zusatzinfos!
Gern geschehen. Feedback und Fragen sind weiterhin erwünscht! 8)


-----------------------------------------------------------------------

Ich habe den Code im Startpost aktualisiert, es gab ein kleines Problem im Zusammenspiel der beiden Suchroutinen, welches behoben wurde, außerdem wurde dem Demonstrationsprogramm eine einfache Waitfunktion hinzugefügt um den Prozessor überflüssige Last abzunehmen (hat nichts mit der Wegfindungsroutine zu tun).
#1106
FINDPATH() hat mit meiner Funktion erstmal nichts zu tun.

Jedoch habe ich sie mir als Vorbild genommen und die Parameter und den Rückgabewert fast identisch gestaltet, wodurch mir eine Erklärung der derselben hoffentlich abgenommen wurde und der Nutzer sich schnell hinein findet.

Wenn du eine Karte hast, die von FINDPATH() gelesen werden kann, dann kann sie auch von meiner Funktion gelesen werden. Welche der beiden Funktionen du dann verwendest entscheidet sich an Hand deiner Anforderungen:

Suchst du eine Funktion, die:

  • in 4 Richtungen sucht -> verwende Gernots FINDPATH()
  • in 8 Richtungen sucht -> verwende Init_AdvancedFindpath() und Advanced_FindPath()
  • von dir selbst an weitere Parameter oder zusätzliche Karten angepasst werden kann -> verwende Init_AdvancedFindpath() und Advanced_FindPath()


Im ersten Post befinden sich eben nicht nur meine beiden Funktionen, sondern diese sind in einem Beispiel-Projekt eingearbeitet, welches den gleichen Weg mit beiden Wegfindungsroutinen sucht und dem Nutzer somit einen Vergleich beider Ergebnisse und ihrer Geschwindigkeiten ermöglicht. In einem Spiel würde natürlich (vermutlich) nur eine der beiden verwendet werden. Das obige Beispiel soll die Vor- und Nachteile demonstrieren und dem Programmierer somit die Entscheidung für eine der beiden vereinfachen. Ich empfehle unbedingt die Verwendung der Mausmitteltaste im Beispiel. ;)

Ich hoffe ich konnte deine Frage beantworten? Weitere Fragen und Verbesserungsvorschläge sind weiterhin erwünscht. ;)

Viele Grüße,
Kanonet.
#1107
Ich habe eine kleine Wegfindungsroutine geschrieben, sie basiert auf dem A*-Algorithmus und ermöglicht die Bewegung in acht Richtungen, also auch diagonal. Wegkosten können beachtet werden.
Aus Geschwindigkeitsgründen erfolgt die Wegsuche gleichzeitig vom Start- und dem Zielpunkt aus, was bei großen Karten zu kleineren Abweichungen in der Mitte führen kann.

Zur Benutzung:
Init_FindPath() einmal ausführen, also vor der Hauptschleife.
Advanced_FindPath() kann dann beliebig oft aufgerufen werden.
Parameter sind ähnlich Gernots FINDPATH(). Zu beachten ist noch, dass beide Routinen im Debugmodus signifikant verlangsamt werden, außerdem dauert die Berechnung um so länger, je mehr begehbare Felder es gibt.

Ich habe mich bemüht eine möglichst hohe Geschwindigkeit zu ermöglichen. Ich würde mich freuen, wenn es dem einen oder anderen nützen würde, und/oder ich weitere Verbesserungsvorschläge bekomme, vielleicht findet sich ja jemand in meine Kommentierung hinein. :D

Viele Grüße.

Hier die Routine in einem kleinen Beispiel, wollt ihr sie selbst nutzen, dann einfach die beiden Funktionen kopieren:

EDIT: ein kleiner aber gravierender Fehler wurde korrigiert. Schaut euch aber auch diesen Algorithmus an und entscheidet dann, was eure Bedürfnisse besser erfüllt.
Code (glbasic) Select
// --------------------------------- //
// Project: Wegfindung
// Autor: Kanonet
// Start: Tuesday, October 19, 2010
// Update: July 23, 2011
// IDE Version: 8.125

LOCAL map[] // dies ist die Karte mit der die Wegfindungsrotiene arbeitet
GLOBAL AFP_mapmax_x%, AFP_mapmax_y% // Größe der Karte; wir von Init_FindPath gesetzt

LOCAL x%, y%, zx%, zy%, lger[], lkan[], mx, my, mr, ml, timeger, timekan, mdown

Init_FindPath(map[], 80,60) // Initialisierung der Wegfindung

SETSCREEN AFP_mapmax_x*10, AFP_mapmax_y*10, 0
SYSTEMPOINTER TRUE

x=0
y=2
zx=AFP_mapmax_x-1
zy=AFP_mapmax_y-1


REPEAT

MOUSESTATE mx, my, mr, ml
IF mr // rechte Maustaste -> Startpunkt setzen
x=mx/10
y=my/10
ELSEIF ml // linke Maustaste -> Zielpunkt setzen
zx=mx/10
zy=my/10
ELSEIF MOUSEAXIS(5) OR KEY(56) // mittlere Maustaste -> passierbarkeit dieses Runkts der Karte ändern
IF mdown=0
mdown=-map[mx/10][my/10]
ENDIF
IF KEY(29) OR KEY (157) THEN mdown=5
map[mx/10][my/10]=mdown
ELSE
mdown=0
ENDIF

IF KEY(57) // Wegsuchen starten und benötigte Zeit messen
timeger=GETTIMERALL()
FINDPATH(map[], lger[], 0.5, x, y, zx, zy)
timeger=GETTIMERALL()-timeger
timekan=GETTIMERALL()
Advanced_FindPath(map[], lkan[], 1, x, y, zx, zy)
timekan=GETTIMERALL()-timekan
ENDIF

FOR i=0 TO AFP_mapmax_x-1 // Karte zeichnen
FOR j=0 TO AFP_mapmax_y-1
IF (map[i][j])<=0 THEN DRAWRECT 10*i, 10*j, 10, 10, RGB(255,255,255)
ALPHAMODE 0.5
IF map[i][j]>1 THEN DRAWRECT 10*i, 10*j, 10, 10, RGB(128,128,128+20*map[i][j])
ALPHAMODE 0
NEXT
NEXT

FOR i=1 TO LEN(lger)-1 // Gernots Weg einzeichnen
DRAWLINE 10*lger[i-1][0]+4,  10*lger[i-1][1]+4,  10*lger[i][0]+4,  10*lger[i][1]+4, RGB(255,0,0)
DRAWRECT 10*lger[i][0]+3,  10*lger[i][1]+3, 3, 3, RGB(255,0,0)
NEXT

FOR i=1 TO LEN(lkan)-1 // Kanonets Weg einzeichnen
DRAWLINE 10*lkan[i-1][0]+3,  10*lkan[i-1][1]+3,  10*lkan[i][0]+3,  10*lkan[i][1]+3, RGB(0,255,0)
DRAWRECT 10*lkan[i][0]+2,  10*lkan[i][1]+2, 3, 3, RGB(0,255,0)
NEXT

PRINT "A", 10*x, 10*y // Startpunkt markieren
PRINT "Z", 10*zx, 10*zy // Zielpunkt markieren

PRINT "Gernot: "+timeger+"ms "+LEN(lger)+" Schritte", 0, 0
PRINT "Kanonet: "+timekan+"ms "+LEN(lkan)+" Schritte", 0, 10

PRINT "[SPACE] - Weg suchen         [Maustasten] - Start/Ziel/Karte verändern", 135, 590

SHOWSCREEN

WAIT(20) // Timer

UNTIL KEY(1)
END



//! Timerfunction - NOT NEEDED FOR PATHFINDING!
//! Its just to slow down the program.
//? Timerfunktion - WIRD NICHT FÜR DIE WEGFINDUNG BENÖTIGT!
//? Dient ledglich dazu, dass Programm zu verlangsamen.
// \param ms - Mainloop will last ms milliseconds. | Hauptschleife wird ms Millisekunden dauern.
// \return Nothing. | Nichts.
FUNCTION WAIT: ms
STATIC WAIT_time
WAIT_time = ms - GETTIMERALL() + WAIT_time
IF WAIT_time<0 THEN WAIT_time=0
SLEEP WAIT_time
WAIT_time=GETTIMERALL()
ENDFUNCTION

//! Initialise the pathfinding. Use it once, before your mainloop.
//? Initialisiert die Wegfindung. Muss einmal ausgeführt werden, am besten vor der Hauptschleife.
// \param AFP_map[] - Map that that will be used for pathfinding. | Karte die durch die Weg genutz werden soll.
// \param x,y  - Size of the Map, you want to create. | Größe der Karte, die erzeugt werden soll.
// \return Nothing, but some global will be set. | Nichts, jedoch werden einige Globals erzeugt.
FUNCTION Init_FindPath: AFP_map[], x, y
GLOBAL AFP_mapmax_x=x
GLOBAL AFP_mapmax_y=y
LOCAL i%
DIM AFP_map[AFP_mapmax_x][AFP_mapmax_y] // Karte dimensionieren
// alle Felder der Karte auf 1 setzen, sie als passierbar machen
SETCURRENTDIR("..")
SETCURRENTDIR("..")
SETCURRENTDIR("Bismarck")
SETCURRENTDIR("Karte")
OPENFILE(1, "karte2.txt", 1)
READLINE 1, AFP_mapmax_y
READLINE 1, AFP_mapmax_y
FOR x=0 TO AFP_mapmax_x-1
FOR y=0 TO AFP_mapmax_y-1
READBYTE 1, i%
AFP_map[x][y]=-2*(i-0.5)
NEXT
NEXT
CLOSEFILE 1

// "Bewegungsrichtung" zur Erstellung weiterer Knoten
GLOBAL AFP_dirx%[], AFP_diry%[], AFP_dirz%[]
DIMDATA AFP_dirx[], 0, 1, 0, -1, 1, 1, -1, -1
DIMDATA AFP_diry[], -1, 0, 1, 0, -1, 1, 1, -1
DIM AFP_dirz[8]
FOR i=0 TO 7
AFP_dirz[i] = (AFP_dirx[i] AND AFP_diry[i])+1
NEXT

// map dient der Ermittlung der Entfernung des jeweils aktuellen Knotens zu Ziel, vorausberechnung erfolgt um Geschwindigkeit gut zu machen
GLOBAL AFP_heuristic%[]
DIM AFP_heuristic[AFP_mapmax_x][AFP_mapmax_y]
FOR y=0 TO AFP_mapmax_y-1
FOR x=0 TO AFP_mapmax_x-1
AFP_heuristic[x][y]=SQR(x*x+y*y)
NEXT
NEXT
ENDFUNCTION

//! This function search a path.
//? Diese Funktion sucht einen Weg.
// \param Look FINDPATH() for details about the parameters! | Details der Parameter können unter FINDPATH() nachgesehen werden.
// \param AFP_map[] - Map, where you search a path. | Karte, durch die ein Weg gefunden werden soll.
// \param heuristik - 0=sortest 1=cheapest Way. | 0=kürzester 1=günstigster Weg.
// \param startx,starty - Startpoint. | Startpunkt.
// \param endx,endy - Destination. | Zielpunkt.
// \return Path length, or 0 if no path was found. | Weglänge, oder 0 falls kein Weg gefunden wurde.
FUNCTION Advanced_FindPath: AFP_map[], loesung[], heuristik, startx%, starty%, endx%, endy%

REDIM loesung[0]

// Validität der Parameter prüfen
IF startx=endx AND starty=endy THEN RETURN 0
IF endx<0 OR endy<0 OR endx>AFP_mapmax_x-1 OR endy>AFP_mapmax_y-1 THEN RETURN 0
IF startx<0 OR starty<0 OR startx>AFP_mapmax_x-1 OR starty>AFP_mapmax_y-1 THEN RETURN 0
IF AFP_map[endx][endy]<=0 THEN RETURN 0
IF AFP_map[startx][starty]<=0 THEN RETURN 0
IF heuristik<0 OR heuristik>1 THEN heuristik=1

STATIC nodemap[] // dient primär der Kennzeichnung bereits besuchter Felder
REDIM nodemap[0]
REDIM nodemap[AFP_mapmax_x][AFP_mapmax_y][2]

STATIC path[] AS TNode // Zwischenspeicher des gefundenen Wegs
REDIM path[0]

// Variablen mit der Endung -s gehören zur Suche von Start, Variablen mit der Endung -g gehören zur Suche vom Ziel
STATIC x%, y%, finish%, cost%, delta%, nodezahls%, nodezahlg%
finish=0; nodezahls%=0; nodezahlg%=0


STATIC opens[] AS TNode // Openlists für noch zu untersuchende Knoten
REDIM opens[1]
STATIC openg[] AS TNode
REDIM openg[1]
STATIC topen AS TNode

STATIC nodes[] AS TNode // Closedlists für abschließend untersuchte Knoten
REDIM nodes[1]
STATIC nodeg[] AS TNode
REDIM nodeg[1]
STATIC tnode AS TNode

// Startpunktdaten einlesen
opens[0].x = startx
opens[0].y = starty
opens[0].cost = 0
opens[0].parent = -1
nodemap[startx][starty][0] = 1

// Zielpunktdaten einlesen
openg[0].x = endx
openg[0].y = endy
openg[0].cost = 0
openg[0].parent = -1
nodemap[endx][endy][0] = 2

REPEAT // Wegsuche beginnen

// Wegsuche ausgehend vom Startpunkt
x = 0; cost = 2147483647
FOREACH topen IN opens[] // Ermittlet günstigsten Open-Knoten
delta = AFP_heuristic[ABS(topen.x-endx)][ABS(topen.y-endy)]
IF topen.cost+delta<cost
cost = topen.cost+delta
y = x
ENDIF
INC x
NEXT
tnode = opens[y]
DIMPUSH nodes[], tnode // übernimmt den Knoten in die Closedlist und löscht ihn aus der Openlist
DIMDEL opens[], y
INC nodezahls

FOR i=0 TO 7 // Untersuchung der 8 benachbarten Knoten
x = tnode.x + AFP_dirx[i]
y = tnode.y + AFP_diry[i]
IF x>-1 AND y>-1 AND x<AFP_mapmax_x AND y<AFP_mapmax_y // Knoten liegt auf der Karte
    IF AFP_map[x][y]>0 AND AFP_map[x][tnode.y]>0 AND AFP_map[tnode.x][y]>0 // Knoten ist begehbar
IF nodemap[x][y][0]=0 // Knoten wurde noch nicht besucht
topen.x = x
topen.y = y
topen.parent = nodezahls
topen.cost = tnode.cost + heuristik * (AFP_map[x][y]-1)  + AFP_dirz[i]
DIMPUSH opens[], topen // erzeuge neuen Openknoten
nodemap[x][y][0]=1
nodemap[x][y][1]=nodezahls
ELSEIF nodemap[x][y][0]=2 // Knoten wurde bereits durch die andere Suchroutine erreicht -> Suche erfolgreich
topen.x = x
topen.y = y
topen.parent = nodezahls
topen.cost = tnode.cost + heuristik * (AFP_map[x][y]-1)  + AFP_dirz[i] + nodeg[nodemap[x][y][1]].cost
DIMPUSH path[], topen // erzeuge neuen Openknoten
finish = 1
ENDIF
ENDIF
ENDIF
NEXT

IF NOT LEN(opens[]) THEN finish=3 // Wenn kein Openknoten mehr existiert -> keine Verbindung zwischen Start und Ziel möglich -> Suche abbrechen

IF finish THEN BREAK

// Wegsuche ausgehend vom Zielpunkt
x = 0; cost = 2147483647
FOREACH topen IN openg[] // Ermittlet günstigsten Open-Knoten
delta = AFP_heuristic[ABS(topen.x-startx)][ABS(topen.y-starty)]
IF topen.cost+delta<cost
cost = topen.cost+delta
y = x
ENDIF
INC x
NEXT
tnode = openg[y]
DIMPUSH nodeg[], tnode // übernimmt den Knoten in die Closedlist und löscht ihn aus der Openlist
DIMDEL openg[], y
INC nodezahlg

FOR i=0 TO 7 // Untersuchung der 8 benachbarten Knoten
x = tnode.x + AFP_dirx[i]
y = tnode.y + AFP_diry[i]
IF x>-1 AND y>-1 AND x<AFP_mapmax_x AND y<AFP_mapmax_y // Knoten liegt auf der Karte
IF AFP_map[x][y]>0 AND AFP_map[x][tnode.y]>0 AND AFP_map[tnode.x][y]>0 // Knoten ist begehbar
IF nodemap[x][y][0]=0 // Knoten wurde noch nicht besucht
topen.x = x
topen.y = y
topen.parent = nodezahlg
topen.cost = tnode.cost + heuristik * (AFP_map[x][y]-1)  + AFP_dirz[i]
DIMPUSH openg[], topen // erzeuge neuen Openknoten
nodemap[x][y][0]=2
nodemap[x][y][1]=nodezahlg
ELSEIF nodemap[x][y][0]=1 // Knoten wurde bereits durch die andere Suchroutine erreicht -> Suche erfolgreich
topen.x = x
topen.y = y
topen.parent = nodemap[x][y][1]
topen.cost = tnode.cost + heuristik * (AFP_map[x][y]-1)  + AFP_dirz[i] + nodes[topen.parent].cost
DIMPUSH path[], topen // erzeuge neuen Openknoten
nodemap[x][y][0]=2
nodemap[x][y][1]=nodezahlg
finish = 1
ENDIF
ENDIF
ENDIF
NEXT

IF NOT LEN(openg[]) THEN finish=3 // Wenn kein Openknoten mehr existiert -> keine Verbindung zwischen Start und Ziel möglich -> Suche abbrechen

UNTIL finish // Wegsuche beendet

IF finish=3 THEN RETURN 0 // es wurde kein Weg gefunden

    x = 0; y=0; cost = 2147483647
FOREACH topen IN path[] // Ermittlet günstigsten Open-Knoten
IF topen.cost<cost
cost = topen.cost
y = x
ENDIF
INC x
NEXT
topen = path[y]
cost=topen.parent
delta=nodemap[topen.x][topen.y][1]
REDIM path[0]
DIMPUSH path[], topen

tnode = nodes[cost] // gefundenen Pfad vom Startpunkt aus auswerten
WHILE tnode.parent <> -1
DIMPUSH path[], tnode
tnode = nodes[tnode.parent]
WEND
DIMPUSH path[], tnode
REDIM nodes[0]

cost = LEN(path[])
REDIM loesung[cost][2]
FOR i=cost-1 TO 0 STEP -1
loesung[i][0] = path[cost-i-1].x
loesung[i][1] = path[cost-i-1].y
NEXT
REDIM path[0]

IF nodezahlg // gefundenen Pfad vom Zielpunkt aus auswerten
tnode = nodeg[delta]
WHILE tnode.parent <> -1
delta=tnode.parent
DIMPUSH path[], tnode
tnode = nodeg[tnode.parent]
WEND
DIMPUSH path[], tnode
REDIM nodeg[0]

delta = cost+LEN(path[])
REDIM loesung[delta][2]
FOR i=delta-1 TO cost STEP -1
loesung[i][0] = path[i-cost].x
loesung[i][1] = path[i-cost].y
NEXT
REDIM path[0]
ENDIF

RETURN delta

ENDFUNCTION

// Knoten, ein (möglicher) Schritt zum Ziel
TYPE TNode
cost% // bisherige Wegkosten
parent% // ID des Vorgängerknotens
x%
y%
ENDTYPE
#1108
Hallo,

habe hier mal ein kleines Testprojekt um ein paar Probleme zu dokumentieren, auf die ich gestoßen bin. Das Bild zeigt was ich meine:

1. Die gesamte Szene ist zu dunkel. Der Untergrund bspw. ist eigentlich weiß! Wenn ich kein Licht erzeuge ist alles ok, d.h. hell, erzeuge ich aber ein Licht (z.B. um Schatten erzeugen zu können), ist es so dunkel wie im Bild. Vermutlich ist das kein Bug, sondern nur ein Fehler meinerseits? Bitte um Hilfe.
2. Der Schattenwurf ist vollkommen falsch, es wirkt als würde das Licht viel niedriger hängen, als es hängt. Mir ist aufgefallen, dass dies nur bei skaliertem Mesh auftritt, behalte ich die Ursprungsgröße bei, sind die Schatten korrekt.
3. Eine "Lücke" im Schatten. Ich habe keine Ahnung, wie das kommt, aber mir ist folgendes aufgefallen: importiere ich das Objekt aus .md2 oder .obj tritt diese Lücke (und andere Fehler) auf, importiere ich aus .3ds gibt es diese Lücke nicht. Möglicherweise ein Fehler im 3d-Converter?

Beispielprojekt ist angehangen.

Vielen Danke.

[attachment deleted by admin]
#1109
Ich habe dem Entitysystem bei mir eine Funktion hinzugefügt, mit der man (ddd-)Objekte auf eine bestimmte Größe skalieren kann. Im Gegensatz zu EntityScale gibt man dabei keinen Skalierungsfaktor an, sondern die gewünschte (absolute) Größe des Objekts, es ist also nicht nötig die genaue Ursprungsgröße zu kennen.
Da die Funktion lediglich selbst die entsprechenden Skalierungsfaktoren ausrechnet und dann spiechert, sollte sie keine Konflikte oder Verlangsamungen innerhalb des ES auslösen und sich somit perfekt integrieren. Einzig zu beachten wäre: Animationen werden vermutlich ignoriert, bzw. ich habe keine Ahnung, wie die Funktion auf eine animiertes Objekt wirken würde.

Vielleicht nützt es ja dem einen oder anderen, daher hier der Code:
Code (glbasic) Select
// ------------------------------------------ //
//! Scales an entity. Object will fit in an Box with the Size sx, sy, sz.
//! The scaling is only applied TO the final entity.
//! Parents and childs will not be affected at all.
// \param entity - entity to an entity
// \param sx,sy,sz - new size if the object
// ------------------------------------------ //
FUNCTION EntityFit%: entity%, sx,sy,sz
IF EntityIsValid(entity)=FALSE THEN RETURN
LOCAL count, face[]
count = X_NUMFACES( g3DObj[entity].ddd )
LOCAL maxX = -1000000000
LOCAL maxY = -1000000000
LOCAL maxZ = -1000000000
LOCAL minX = 1000000000
LOCAL minY = 1000000000
LOCAL minZ = 1000000000
FOR i = 0 TO count - 1
X_GETFACE g3DObj[entity].ddd , 0 , i , face[]
IF minX > ( face[ 0 ][ 0 ] ) THEN minX = ( face[ 0 ][ 0 ] )
IF minX > ( face[ 1 ][ 0 ] ) THEN minX = ( face[ 1 ][ 0 ] )
IF minX > ( face[ 2 ][ 0 ] ) THEN minX = ( face[ 2 ][ 0 ] )
IF minY > ( face[ 0 ][ 1 ] ) THEN minY = ( face[ 0 ][ 1 ] )
IF minY > ( face[ 1 ][ 1 ] ) THEN minY = ( face[ 1 ][ 1 ] )
IF minY > ( face[ 2 ][ 1 ] ) THEN minY = ( face[ 2 ][ 1 ] )
IF minZ > ( face[ 0 ][ 2 ] ) THEN minZ = ( face[ 0 ][ 2 ] )
IF minZ > ( face[ 1 ][ 2 ] ) THEN minZ = ( face[ 1 ][ 2 ] )
IF minZ > ( face[ 2 ][ 2 ] ) THEN minZ = ( face[ 2 ][ 2 ] )
IF maxX < ( face[ 0 ][ 0 ] ) THEN maxX = ( face[ 0 ][ 0 ] )
IF maxX < ( face[ 1 ][ 0 ] ) THEN maxX = ( face[ 1 ][ 0 ] )
IF maxX < ( face[ 2 ][ 0 ] ) THEN maxX = ( face[ 2 ][ 0 ] )
IF maxY < ( face[ 0 ][ 1 ] ) THEN maxY = ( face[ 0 ][ 1 ] )
IF maxY < ( face[ 1 ][ 1 ] ) THEN maxY = ( face[ 1 ][ 1 ] )
IF maxY < ( face[ 2 ][ 1 ] ) THEN maxY = ( face[ 2 ][ 1 ] )
IF maxZ < ( face[ 0 ][ 2 ] ) THEN maxZ = ( face[ 0 ][ 2 ] )
IF maxZ < ( face[ 1 ][ 2 ] ) THEN maxZ = ( face[ 1 ][ 2 ] )
IF maxZ < ( face[ 2 ][ 2 ] ) THEN maxZ = ( face[ 2 ][ 2 ] )
NEXT
g3DObj[entity].sx=sx/ABS(maxX-minX)
g3DObj[entity].sy=sy/ABS(maxY-minY)
g3DObj[entity].sz=sz/ABS(maxZ-minZ)
ENDFUNCTION


(Die Grundsätzliche Idee der Umsetzung stammt aus Helmlos' Analyse3DObject)