Netzwerk

Anfängerfragen zum Programmieren mit PureBasic.
Pandorra
Beiträge: 124
Registriert: 10.02.2007 12:15

Beitrag von Pandorra »

thx.
Ich werde es auch so machen das er nur die Tastatureingaben sendet und ale sagen wir mal 30 sek testet er ob die Positionen übereinstimmen( wenn nicht wird die Figur zu der richtigen Psoition vom Server gesetzt).
So ähnlich wie bei den meisten online spielen.
EVTL. werde ich die Tastatur eingaben als Variable verschicken. Escape = 1 ...
Benutze PB v 4.40 Beta 3
Benutzeravatar
ZeHa
Beiträge: 4760
Registriert: 15.09.2004 23:57
Wohnort: Friedrichshafen
Kontaktdaten:

Beitrag von ZeHa »

Hmmm also ich hoffe daß ich das jetzt eingermaßen erklären kann. Also wenn Du zeitgesteuert programmieren willst, dann bräuchtest Du eine getPlayerX()-Funktion, die Du mit einem Zeitparameter aufrufen kannst. Somit kannst Du die X-Position zu jedem beliebigen Zeitpunkt aufrufen und alle Situationen rekonstruieren, das ist wichtig wenn Du Server und Client einigermaßen synchron haben willst.

Das Prinzip ist das folgende:

1) Du speicherst NICHT die aktuelle Position des Spielers, sondern immer nur die zuletzt geänderte Position, zusammen mit dem Zeitpunkt. Deine Spieler-Struktur besitzt also auf jeden Fall eine xPos.f, eine yPos.f, und eine lastTime.l

2) Sobald der Spieler eine Taste drückt bzw. die Figur ihre Richtung wechselt (kann auch für CPU-Gegner gelten, die ja nicht tastaturgesteuert sind), rufst Du eine Funktion savePosition(*p.player, currentTime.l) auf, die sich die aktuelle Position merkt (also in xPos.f und yPos.f hineinschreibt) und die aktuelle Zeit ebenfalls (lastTime = currentTime).

3) Wenn Du nun die Position benötigst, sei es für das Anzeigen oder die Erkennung von Kollisionen, rufst Du getPlayerX(*p.player, currentTime.l) bzw. getPlayerY(...) auf. Diese rechnen einfach anhand der zuletzt gespeicherten Position die aktuelle Position aus, denn sie wissen ja die Richtung, die Geschwindigkeit und die Zeit, die seitdem vergangen ist.

Somit hast Du jederzeit Zugriff auf beliebige Zeitpunkte, kannst also auch ein Stückweit in die Zukunft und in die Vergangenheit sehen. Natürlich nur im Millisekundenbereich und natürlich ist das auch nicht immer 100% korrekt, aber es ist deutlich besser als andere Verfahren, wo Du eben einfach immer nur einen Wert auf die Position aufaddierst.
Die momentane Zeit erfragst Du einfach durch TimeGetTime oder ElapsedMilliseconds etc. und übergibst sie an Deine levelUpdate()-Funktion oder was auch immer Du da hast.

Hier ein paar Beispiele für die Funktionen (entnommen aus meinem derzeitigen Projekt, daher stimmen jetzt die Namen nicht so wie oben):

Code: Alles auswählen

Procedure cachePos(*o.MovingObject, time)
  *o\x = getX(*o, time)
  *o\y = getY(*o, time)
  *o\lastTime = time
EndProcedure

Procedure moveLeft(*o.MovingObject, time)
  If *o\xdir <> -1
    cachePos(*o, time)
    *o\xdir = -1
    *o\facing = #DIR_LEFT
  EndIf
EndProcedure

Procedure stopLeft(*o.MovingObject, time)
  If *o\xdir = -1
    cachePos(*o, time)
    *o\xdir = 0
  EndIf
EndProcedure

Procedure.f getX(*o.movingobject, time)
  ProcedureReturn *o\x + (time - *o\lastTime) * (*o\speed) * (*o\xdir) / 1000
EndProcedure

Procedure.f getY(*o.MovingObject, time)
  ProcedureReturn *o\y + (time - *o\lastTime) * (*o\speed) * (*o\ydir) / 1000
EndProcedure
Deine Bewegungen sind scheinbar anders, Du benutzt ja Sinus- und Cosinus-Funktionen. Aber auch hier ist es natürlich möglich, das umzusetzen, mußt halt nur die Berechnungen entsprechend anpassen.

----

Nun wieder something completely different ;)
Die Tastatureingaben schickst Du nicht über Escape = 1, KeyDown = 2 oder sowas, sondern Du kannst direkt die Konstante schicken (also z.b. #PB_Key_Escape), die sind ja eindeutig und Du brauchst nicht hin- und herzurechnen.

Das mit den 30 Sekunden ist eigentlich keine schlechte Idee, somit kannst Du sichergehen, daß noch alles paßt.

Zum Thema "muß ich player1, player2, player3 machen": Nein, Du hast alle Player in einem Array oder einer LinkedList und kannst dann ForEach bzw. eine normale For-Schleife verwenden. Am besten eine updatePlayer()-Funktion basteln, die ihrerseits weitere Funktionen aufruft, und an diese eine Funktion dann den Pointer übergeben und per Schleife halt mehrmals mit unterschiedlichen Pointern aufrufen.

----

Und jetzt noch ein ganz anderer Tip. Programmiere erstmal einen kleinen Pong-Clone und mach den Netzwerkfähig (mit den oben beschriebenen Zeit-Funktionen). Wenn Du das 100% hinkriegst, würd ich erst mit dem eigentlichen Spiel weitermachen, denn es ist schwerer als man denkt (ich hab auch mal ein Network-Pong gemacht und das ist voller Fehler ;) ).
Bild     Bild

ZeHa hat bisher kein Danke erhalten.
Klicke hier, wenn Du wissen möchtest, woran ihm das vorbeigeht.
Benutzeravatar
ZeHa
Beiträge: 4760
Registriert: 15.09.2004 23:57
Wohnort: Friedrichshafen
Kontaktdaten:

Beitrag von ZeHa »

Ach ja ich hatte noch was vergessen:
Also wenn Du nun die Daten übers Netzwerk schickst, brauchst Du im Grunde immer nur alle Richtungswechsel zu verschicken. Ein Richtungswechsel besteht in diesem Fall nur aus der neuen Richtung und dem Zeitpunkt des Wechsels (und natürlich der ID des Players / Gegners / Objekts). Das sollte eigentlich genügen, denn der Server, der das Spiel ja verwaltet, braucht nun nur noch die momentane Position und Zeit speichern (mit savePos (bzw. cachePos)) und dann den Richtungswechsel ausführen.

Da ich Dein Projekt nicht kenne, Du aber mit Sinus und Cosinus arbeitest, könnte es evtl. sogar korrekter sein, tatsächlich die Tastatureingaben zu schicken, allerdings würde ich dann immer nur die KeyPushed und KeyReleased-Events verschicken, das sind dann pro Bewegung meist 2 Events. Also nicht auf die Idee kommen und in jedem Frame, wo ein Key gepushed ist, dies als Event verschicken ;)
Bild     Bild

ZeHa hat bisher kein Danke erhalten.
Klicke hier, wenn Du wissen möchtest, woran ihm das vorbeigeht.
Pandorra
Beiträge: 124
Registriert: 10.02.2007 12:15

Beitrag von Pandorra »

bisher hab ich

Code: Alles auswählen

         For i = 0 To player
         CameraLocate  (0, *players(1)\xPos - Cos(2*#PI*(rot/360))*15, TerrainHeight(*players(1)\xPos,*players(1)\zPos)+10, *players(1)\zPos + Sin(2*#PI*(rot/360))*15)
         EntityLocate(0, *players(1)\xPos,TerrainHeight(*players(1)\xPos,*players()\zPos),*players(1)\zPos)
         Next
und der ist ja auch nicht ganz richtig(bei 2D klappt es nun will ich es aber auch für später 3D haben)
Wie krieg ich es richtig hin?

Code: Alles auswählen

*player/xPos 
:nimmt er nicht
wie kann ich dann einstellen das er nach der Player anzahl geht?
Benutze PB v 4.40 Beta 3
Benutzeravatar
ZeHa
Beiträge: 4760
Registriert: 15.09.2004 23:57
Wohnort: Friedrichshafen
Kontaktdaten:

Beitrag von ZeHa »

Hmm also bau Dir erstmal die folgenden Funktionen:

Code: Alles auswählen

Procedure getPlayerX(*p.player, currentTime)
  ProcedureReturn *p\xPos - Cos(2 * #PI * (rot/360)) * 15
EndProcedure

; ...und das gleiche für Y und Z
Die Berechnung stimmt zwar nicht, aber da ich in Mathe nicht grad der Oberfreak bin kann ich Dir hierbei auch nicht richtig helfen. Auf jeden Fall mußt Du noch den Zeitfaktor (currentTime - (*p\lastTime)) und die Geschwindigkeit (*p\movingSpeed) einbauen. Da kann Dir bestimmt einer unserer Mathematiker besser helfen ;)
Prinzipiell mußt Du nur Zeitfaktor * Geschwindigkeit rechnen und diesen Wert auf die momentane Position hinzuaddieren, aber wie das Sinus- und Cosinus-mäßig läuft, keine Ahnung.

Dann machst Du folgendes:

Code: Alles auswählen

Procedure updatePlayer(*p.player, currentTime)
  ; hier drin alle weiteren Funktionen aufrufen,
  ; die den Player updaten, z.B. seine Position ändern,
  ; Kollisionen prüfen, und was weiß ich.
  ; natürlich immer beide Parameter weitergeben!
EndProcedure

Procedure renderPlayer(*p.player, currentTime)
  EntityLocate(*p\entity, getPlayerX(*p, currentTime), getPlayerY(*p, currentTime), getPlayerZ(*p, currentTime))
EndProcedure
Diese Funktionen kannst Du nun mit einer ganz normalen Schleife aufrufen:

Code: Alles auswählen

#NUMBER_OF_PLAYERS = 4
Global Dim *players.player(#NUMBER_OF_PLAYERS -1)

Repeat
  currentTime = ElapsedMilliseconds()

  For i = 0 to #NUMBER_OF_PLAYERS -1
    updatePlayer(*players(i), currentTime)
    renderPlayer(*players(i), currentTime)
  Next i

  ; der Rest Deiner Hauptschleife
  ; ...
Forever
Das gleiche kannst Du nun mit jeder beliebigen Entity machen, also auch mit Gegnern usw. Da sich deren elementare Funktionen (Position setzen, rendern, etc) i.d.R. kaum unterscheiden, solltest Du sie auf jeden Fall von der gleichen Struktur abstammen lassen.
Also Du machst z.B. sowas:

Code: Alles auswählen

Structure movingObject
  xPos.f
  yPos.f
  zPos.f
  lastTime.l
  entity.l     ; hier drin speicherst Du die Nummer der 3D-Entity

  ; ... und noch alles weitere, was ALLE benötigen!
EndStructure

; davon leitest Du nun den Player ab:
Structure Player extends movingObject
  lives.b
  score.l
  energy.l
EndStructure

; und beliebig viele andere Dinge:
Structure BadGuy extends movingObject
  evilness.l
  scoreValue.l
EndStructure
Nun kannst Du die Funktionen, die für alle gelten sollen, nicht mit *p.player machen, sondern mit *o.movingObject. Trotzdem kannst Du alles übergeben! Das heißt, allgemeine Funktionen werden allgemein programmiert, aber ob Du nun einen Player oder BadGuy übergibst, spielt keine Rolle, da beide vom selben Basis-Objekt abstammen und die Funktion nur nach dem Basis-Objekt verlangt! Cool was :mrgreen:
Bild     Bild

ZeHa hat bisher kein Danke erhalten.
Klicke hier, wenn Du wissen möchtest, woran ihm das vorbeigeht.
Pandorra
Beiträge: 124
Registriert: 10.02.2007 12:15

Beitrag von Pandorra »

danke.
Und die Positionen die ich mit getx... ermittelt habe wie soll ich die so festlegen das die dort starten. bsp.(wie davor:

Code: Alles auswählen

Global Dim *players.player(#NUMBER_OF_PLAYERS -1)
*players(1) = newPlayer(300, 300)
?

schonmal danke für die antworten im vorraus, sonst klappt alles.
Benutze PB v 4.40 Beta 3
Benutzeravatar
ZeHa
Beiträge: 4760
Registriert: 15.09.2004 23:57
Wohnort: Friedrichshafen
Kontaktdaten:

Beitrag von ZeHa »

Ganz einfach, Du bastelst Dir eine newPlayer()-Funktion:

Code: Alles auswählen

Procedure.l newPlayer(x, y, z, time)
  *new = AllocateMemory(SizeOf(player))

  setPlayerX(*new, x, time)
  setPlayerY(*new, y, time)
  ; und anderen initialisierungskram...

  ProcedureReturn *new
EndProcedure
Die setPlayerX()-Funktion muß aber auch wieder 100% sicher sein, d.h. hier mußt Du die lastTime des Players dann wiederum auf die aktuelle Zeit setzen usw.
Bild     Bild

ZeHa hat bisher kein Danke erhalten.
Klicke hier, wenn Du wissen möchtest, woran ihm das vorbeigeht.
Pandorra
Beiträge: 124
Registriert: 10.02.2007 12:15

Beitrag von Pandorra »

Es kalppt nu bis auf eine kleinigkeit.

Code: Alles auswählen

Procedure getPlayerX(*p.player, currentTime)
  ProcedureReturn *p\xPos
EndProcedure 

Procedure getPlayerY(*p.player, currentTime)
  ProcedureReturn *p\yPos
EndProcedure 

Procedure getPlayerZ(*p.player, currentTime)
  ProcedureReturn *p\zPos
EndProcedure 
ist Invalid memoryacess.
was kann ich dagegen machen?
Benutze PB v 4.40 Beta 3
Benutzeravatar
#NULL
Beiträge: 2238
Registriert: 20.04.2006 09:50

Beitrag von #NULL »

gib den rückgabewert der procedures an (im proc-kopf)
my pb stuff..
Bild..jedenfalls war das mal so.
Pandorra
Beiträge: 124
Registriert: 10.02.2007 12:15

Beitrag von Pandorra »

wie jetzt?
Hier sind bestimmt einige fehler:

Code: Alles auswählen


#NUMBER_OF_PLAYERS = 4

Structure movingObject
  xPos.f
  yPos.f
  zPos.f
  lastTime.l
  entity.l  
EndStructure

Structure Player Extends movingObject
  lives.b
  score.l
  energy.l
EndStructure 




Procedure getPlayerX(*p.player, x, currentTime)
  ProcedureReturn *p\xPos
EndProcedure 

Procedure getPlayerY(*p.player, y, currentTime)
  ProcedureReturn *p\yPos
EndProcedure 

Procedure getPlayerZ(*p.player, z, currentTime)
  ProcedureReturn *p\zPos
EndProcedure 

Procedure.l newPlayer(x, z, y, time)
  *new = AllocateMemory(SizeOf(player))

  getPlayerX(*p.player, x, time)
  getPlayerZ(*p.player, z, time)
  getPlayerY(*p.player, y, time)

  ProcedureReturn *new
EndProcedure 


Global Dim *players.player(#NUMBER_OF_PLAYERS -1)
*players(1) = newPlayer(300, 300, 200, currentTime)


Procedure renderPlayer(*p.player, currentTime)
  CameraLocate  (0, getPlayerX(*p.player, x, currentTime) - Cos(2*#PI*(rot/360))*15, TerrainHeight(getPlayerX(*p.player, x, currentTime),getPlayerZ(*p.player, z, currentTime))+10, getPlayerZ(*p.player, z, currentTime) + Sin(2*#PI*(rot/360))*15)
  EntityLocate(*p\entity, getPlayerX(*p.player, x, currentTime), TerrainHeight(getPlayerX(*p.player, x, currentTime),getPlayerZ(*p.player, z, currentTime)), getPlayerZ(*p.player, z, currentTime)) 
EndProcedure 



;...
Repeat
    currentTime = ElapsedMilliseconds()

    For i = 0 To #NUMBER_OF_PLAYERS -1
      renderPlayer(*players(i), currentTime)
    Next i 
        
Wenn ich diese Probleme soweit geschaft habe das es läuft werde ich mich um Update Player kümmern
Benutze PB v 4.40 Beta 3
Antworten