Diverse Funktionen für Sechseckraster
Übersicht

n-HalbleiterBetreff: Diverse Funktionen für Sechseckraster |
![]() Antworten mit Zitat ![]() |
|
---|---|---|
Hallo und guten Abend!
Ich hatte es ja schon in meinem Worklog angekündigt hatte, möchte ich ein paar Funktionen vorstellen, die den Leuten helfen sollen, die sich mit Sechseckrastern befassen und diese umsetzen möchten. Ich hoffe, ich kann hier helfen. ![]() Zuallererst: Wie unterscheidet sich ein Sechseckraster von einem Viereckraster? Der auffälligste Unterschied: Es hat sechs anstatt vier Ecken, dadurch eine andere Form, was darin resultiert, dass wir 6 Nachbarn haben, und nicht 8. Erschwerend kommt noch hinzu, dass die Nachbarn anders angeordnet sind als bei einem quadratischen Raster. Diesen Umstand verdeutlicht diese Grafik: Auch ist es eine Umstellung, dass man anders Kacheln muss. Bei einem quadratischen Raster geht man einfach stur von oben links nach unten rechts und malt bei CX*Tilebreite bzw. CY*Tilehöhe. Bei einem Sechseckraster wird jede Kachel, die einen ungeraden U-Wert hat (U und V ist hier X bzw. Y im Array), um eine halbe Kachelhöhe nach oben gesetzt wird. Auch gibt es ein paar Unterschiede bei dem horizontalen Abstand. Zu dem Problem mt dem Versetzen habe ich auch eine Grafik angefertigt: Und als sei das noch nicht genug, gibt es hier direkt einen Code, mit dem das Kacheln übernommen wird. Code: [AUSKLAPPEN] Function TileGrid(ShowText=0)
Local CX,CY,AX,AY Local XStep=(HexGrid_Length_Middle/2)+(HexGrid_Length_UpLo/2),YStep=HexGrid_Height,YStepH=YStep/2 Color 255,255,255 For CX=0 To HexGrid_Info_Width-1 For CY=0 To HexGrid_Info_Height-1 ;Aktuelle Positionen zwischenspeichern, das Konstrukt YStep-(...) sorgt für den Verschub nach oben AX=SchubX+(CX+1)*XStep:AY=SchubY+(CY+1)*YStep-((CX Mod 2)*(YStepH)) DrawImage GridImage,AX,AY If ShowText=1 Then Text AX,AY,CX+","+CY,1,1 Next Next End Function Der Part mit dem "ShowText" kann an sich auch weggelassen werden, allerdings werde ich ihn in dem Beispiel, das noch kommt, gebrauchen. Eine kurze Erläuterung: CX und CY sind die Zählvariablen, HexGrid_Info_Width bzw. HexGrid_Info_Height sind Konstanten (für Spiele oder Anwendungen wären Globale besser gewesen, aber dies ist ja nur zum Testen), die die Breite und Höhe angeben. AX uns AY sind die aktuelle Position des Tiles GridImage ist das Kachelbild. Dann gibt es noch XStep YStep und YStepH. Diese sind die horizontalen/vertikalen/halben vertikalen Abstände der Kacheln untereinander. Ich denke, was die Berechnungen machen, dürfte klar sein. Die letzten Variablen SchubX und SchubY speichern die Einrückung in Pixeln. Das habe ich zu Testzwecken eingebaut. Dann ist das nächste Problem das mit den Nachbarn. Hierzu muss man sich noch einmal die "Bauweise" des Gitters vor Augen führen: Jede Kachel hat einen Nachbarn oben (U,V-1) und einen unten (U,V+1). Jetzt kommt der vertikale Abstand jedes zweiten Tiles in die Quere: Ist U gerade ("U Mod 2 = 0"), dann sind das andere, als wenn U ungerade ist. Dazu habe ich natürlich auch eine Funktion geschrieben, die das berücksichtigt. Die Nachbarn sind bei geradem U bei ![]() ![]() ![]() ![]() und bei ungeradem U ![]() ![]() ![]() ![]() Der Code ist folgender: Code: [AUSKLAPPEN] ;Formt die Eingabe in eine gültige Form um, z.B. wenn die Eingabe außerhalb der Map sein würde.
;Hier wird die Karte Torusförmig behandelt. Function TransformToValid(Inp,XY=0) Local margin If XY=0 margin=HexGrid_Info_Width Else margin=HexGrid_Info_Height End If Return (Inp+margin)Mod(margin) End Function Dim Neighbour(5,1);Die Nachbarn des Feldes, gegen den Uhrzeigersinn, angefangen oben links ; Elemente: (Nachbar, X 0/Y 1) Function GetNeighbours(U,V) Local UIsStraight=(U Mod 2) Neighbour(1,0)=TransformToValid(U):Neighbour(1,1)=TransformToValid(V-1,1) Neighbour(4,0)=TransformToValid(U):Neighbour(4,1)=TransformToValid(V+1,1) If UIsStraight Neighbour(0,0)=TransformToValid(U-1):Neighbour(0,1)=TransformToValid(V-1,1) Neighbour(5,0)=TransformToValid(U-1):Neighbour(5,1)=TransformToValid(V,1) Neighbour(2,0)=TransformToValid(U+1):Neighbour(2,1)=TransformToValid(V-1,1) Neighbour(3,0)=TransformToValid(U+1):Neighbour(3,1)=TransformToValid(V,1) Else Neighbour(0,0)=TransformToValid(U-1):Neighbour(0,1)=TransformToValid(V,1) Neighbour(5,0)=TransformToValid(U-1):Neighbour(5,1)=TransformToValid(V+1,1) Neighbour(2,0)=TransformToValid(U+1):Neighbour(2,1)=TransformToValid(V,1) Neighbour(3,0)=TransformToValid(U+1):Neighbour(3,1)=TransformToValid(V+1,1) End If End Function Die Funktion "TransformToValid" wird oben schon beschrieben. An sich lassen sich jetzt die meisten Problembereiche abdecken. Das dritte Problem ist die Sache mit dem Selektieren, per Maus zum Beispiel. Man kann nun mittels komplizierter mathematischer Berechnungen (oder mit dreimaligem "CheckQuad3D", wenn man die D3D benutzt) bestimmen, in welchem Feld sich der Cursor befindet. Da jedoch die "Mathe-Methode" recht aufwendig ist, können wir einen Kompromiss in Form einer Annäherung eingehen: Wir prüfen für jedes Feld, ob sich die angegebenen Koordinaten in einem gewissen Abstand befinden. Wenn jetzt dieser Abstand gut gewählt ist, dann ist das Ergebnis ziemlich genau dem exakten, und schneller. Hierzu habe ich auch eine Funktion vorbereitet: Code: [AUSKLAPPEN] ;Eine einfache Funktion, die zurückgibt, ob sich zwei Objekte in einem gewissen Abstand zueinander befinden.
Function InRange(X1#,Y1#,X2#,Y2#,Range) Return (((X1-X2)*(X1-X2)+(Y1-Y2)*(Y1-Y2))*((X1-X2)*(X1-X2)+(Y1-Y2)*(Y1-Y2)))<=(Range*Range) End Function Global SelectedX=5,SelectedY=8 Function SelectedTileAt(X,Y) Local CX,CY,AX,AY Local XStep=(HexGrid_Length_Middle/2)+(HexGrid_Length_UpLo/2),YStep=HexGrid_Height,YStepH=YStep/2 For CX=0 To HexGrid_Info_Width-1 For CY=0 To HexGrid_Info_Height-1 AX=SchubX+(CX+1)*XStep:AY=SchubY+(CY+1)*YStep-((CX Mod 2)*(YStepH)) If InRange(X,Y,AX,AY,HexGrid_DiagLength) Then SelectedX=CX:SelectedY=CY:Return Next Next End Function Achja, was die ganze Zeit an Variablen genutzt, aber nicht dokumentiert wurde: Code: [AUSKLAPPEN] Const HexGrid_Height=55
Const HexGrid_Length_Middle=70 Const HexGrid_Length_UpLo=30 Const HexGrid_DiagLength=HexGrid_Length_Middle*10 Diese ganzen Variablen werden dazu benötigt, um die erforderlichen Zwischenwerte zu errechnen. Die Variable HexGrid_DiagLength ist der minimal benötigte Abstand, um ein Tile auszuwählen, HexGrid_Length_Middle und HexGrid_Length_UpLo sind die Längen in der Mitte (zwischen den beiden äußersten Punkten) bzw. die der Strecke oben und unten. HexGrid_Height ist die Höhe einer Kachel. So, um das alles nochmal ein wenig in Richtung Praxis zu bringen und direkt ein Beispiel zu liefern, gibt es noch diesen Code: [AUSKLAPPEN] Graphics 800,600,32,2
AutoMidHandle True SetBuffer BackBuffer() Const HexGrid_Height=55 Const HexGrid_Length_Middle=70 Const HexGrid_Length_UpLo=30 Const HexGrid_DiagLength=HexGrid_Length_Middle*10 Dim HexPoints(5,1);Punkte gegen den Uhrzeigersinn, angefangen oben links HexPoints(0,0)=-(HexGrid_Length_UpLo/2):HexPoints(0,1)=-(HexGrid_Height/2);OL HexPoints(1,0)=-(HexGrid_Length_Middle/2):HexPoints(1,1)=0;L HexPoints(2,0)=-(HexGrid_Length_UpLo/2):HexPoints(2,1)=(HexGrid_Height/2);UL HexPoints(3,0)=(HexGrid_Length_UpLo/2):HexPoints(3,1)=(HexGrid_Height/2);UR HexPoints(4,0)=(HexGrid_Length_Middle/2):HexPoints(4,1)=0;R HexPoints(5,0)=(HexGrid_Length_UpLo/2):HexPoints(5,1)=-(HexGrid_Height/2);OR Global HexGrid_Info_Width=15,HexGrid_Info_Height=10 Global SchubX=(-(HexGrid_Length_Middle/2)+(HexGrid_Length_UpLo/2)),SchubY=0 Global GridImage=CreateGridImage() ;Hier wird das Rasterbild erstellt Function CreateGridImage() Local C,Img,Width=HexGrid_Length_Middle/2,Height=HexGrid_Height/2 Img=CreateImage(HexGrid_Length_Middle,HexGrid_Height) SetBuffer ImageBuffer(Img) Color 255,255,255 ;Diese kryptische Konstruktion zieht Linien von Punkt 0 zu 1, 1 zu 2, ... 5 zu 0 For C=0 To 5 Line Width+HexPoints(C,0),Height+HexPoints(C,1),Width+HexPoints(((C+1) Mod 6),0),Height+HexPoints(((C+1) Mod 6),1) Next Return Img End Function Dim Neighbour(5,1);Die Nachbarn des Feldes, gegen den Uhrzeigersinn, angefangen oben links ; Elemente: (Nachbar, X 0/Y 1) Function GetNeighbours(U,V) Local UIsStraight=(U Mod 2) Neighbour(1,0)=TransformToValid(U):Neighbour(1,1)=TransformToValid(V-1,1) Neighbour(4,0)=TransformToValid(U):Neighbour(4,1)=TransformToValid(V+1,1) If UIsStraight Neighbour(0,0)=TransformToValid(U-1):Neighbour(0,1)=TransformToValid(V-1,1) Neighbour(5,0)=TransformToValid(U-1):Neighbour(5,1)=TransformToValid(V,1) Neighbour(2,0)=TransformToValid(U+1):Neighbour(2,1)=TransformToValid(V-1,1) Neighbour(3,0)=TransformToValid(U+1):Neighbour(3,1)=TransformToValid(V,1) Else Neighbour(0,0)=TransformToValid(U-1):Neighbour(0,1)=TransformToValid(V,1) Neighbour(5,0)=TransformToValid(U-1):Neighbour(5,1)=TransformToValid(V+1,1) Neighbour(2,0)=TransformToValid(U+1):Neighbour(2,1)=TransformToValid(V,1) Neighbour(3,0)=TransformToValid(U+1):Neighbour(3,1)=TransformToValid(V+1,1) End If End Function ;Ob ein Feld Nachbar ist Function IsNeighbour(U1,V1,U2,V2) Local C GetNeighbours(U1,V1) For C=0 To 5 If Neighbour(C,0)=U2 If Neighbour(C,1)=V2 Return 1 End If End If Next End Function ;Kacheln Function TileGrid(ShowText=0) Local CX,CY,AX,AY Local XStep=(HexGrid_Length_Middle/2)+(HexGrid_Length_UpLo/2),YStep=HexGrid_Height,YStepH=YStep/2 Color 255,255,255 For CX=0 To HexGrid_Info_Width-1 For CY=0 To HexGrid_Info_Height-1 ;Aktuelle Positionen zwischenspeichern, das Konstrukt YStep-(...) sorgt für den Verschub nach oben AX=SchubX+(CX+1)*XStep:AY=SchubY+(CY+1)*YStep-((CX Mod 2)*(YStepH)) DrawImage GridImage,AX,AY If ShowText=1 Then Text AX,AY,CX+","+CY,1,1 Next Next End Function Global SelectedX=5,SelectedY=8 Function SelectedTileAt(X,Y) Local CX,CY,AX,AY Local XStep=(HexGrid_Length_Middle/2)+(HexGrid_Length_UpLo/2),YStep=HexGrid_Height,YStepH=YStep/2 For CX=0 To HexGrid_Info_Width-1 For CY=0 To HexGrid_Info_Height-1 AX=SchubX+(CX+1)*XStep:AY=SchubY+(CY+1)*YStep-((CX Mod 2)*(YStepH)) If InRange(X,Y,AX,AY,HexGrid_DiagLength) Then SelectedX=CX:SelectedY=CY:Return Next Next End Function ;Eine einfache Funktion, die zurückgibt, ob sich zwei Objekte in einem gewissen Abstand zueinander befinden. Function InRange(X1#,Y1#,X2#,Y2#,Range) Return (((X1-X2)*(X1-X2)+(Y1-Y2)*(Y1-Y2))*((X1-X2)*(X1-X2)+(Y1-Y2)*(Y1-Y2)))<=(Range*Range) End Function ;Formt die Eingabe in eine gültige Form um, z.B. wenn die Eingabe außerhalb der Map sein würde. ;Hier wird die Karte Torusförmig behandelt. Function TransformToValid(Inp,XY=0) Local margin If XY=0 margin=HexGrid_Info_Width Else margin=HexGrid_Info_Height End If Return (Inp+margin)Mod(margin) End Function ;Diese Funktion markiert ein Tile farbig Function ColorTile(U,V,R=255,G=0,B=0) Local AX,AY Local XStep=(HexGrid_Length_Middle/2)+(HexGrid_Length_UpLo/2),YStep=HexGrid_Height,YStepH=YStep/2 AX=SchubX+(U+1)*XStep:AY=SchubY+(V+1)*YStep-((U Mod 2)*(YStepH)) Color R,G,B Oval(AX-5,AY-5,10,10,0) End Function ;Testschleife inkl. Variablen SetBuffer BackBuffer() Local Timer=CreateTimer(60),C,ShowGridText=1,ShowNeighbours=1,ShowText=1 While Not KeyHit(1) Cls TileGrid(ShowGridText) SelectedTileAt(MouseX(),MouseY()) SelectedX=TransformToValid(SelectedX) SelectedY=TransformToValid(SelectedY,1) ColorTile(SelectedX,SelectedY) GetNeighbours(SelectedX,SelectedY) If ShowNeighbours For C=0 To 5 ColorTile(Neighbour(C,0),Neighbour(C,1),0,255,0) Next End If If KeyHit(2) Then ShowGridText=ShowGridText=False If KeyHit(3) Then ShowNeighbours=ShowNeighbours=False If KeyHit(4) Then ShowText=ShowText=False If ShowText Color 255,255,255 Text 0,550,MouseX()+","+MouseY()+"->"+SelectedX+","+SelectedY If IsNeighbour(SelectedX,SelectedY,5,6) Text 0,560,"5,6 ist Nachbar von "+SelectedX+","+SelectedY Else Text 0,560,"5,6 ist nicht Nachbar von "+SelectedX+","+SelectedY End If End If Flip 0 WaitTimer Timer Wend End Für sämtliche Funktionen ist dazugeschrieben, für was sie zuständig sind, außerdem habe ich mich bemüht, Ausdrucksstarke Namen der Variablen und Funktionen zu wählen. Mit den Tasten 1,2 und 3 lässt sich ein wenig mit den Modi herumspielen (beschriftetes Raster, farbig markierte Nachbarn, zusätzlich angezeigter Text). Das wär's. Falls sich noch Fragen oder Anregungen ergeben, ich kann euch nicht verbieten, sie mitzuteilen. ![]() |
||
mfg, Calvin
Maschine: Intel Core2 Duo E6750, 4GB DDR2-Ram, ATI Radeon HD4850, Win 7 x64 und Ubuntu 12.04 64-Bit Ploing! Blog "Die Seele einer jeden Ordnung ist ein großer Papierkorb." - Kurt Tucholsky (09.01.1890 - 21.12.1935) |
Übersicht


Powered by phpBB © 2001 - 2006, phpBB Group