Nova has written
In diesem Thread wollen wir nützliche Scripts sammeln.
Wenn ihr mal irgendein Script geschrieben habt um ein Problem zu lösen könnt ihr es hier reinschreiben.
Hihi... da passt ja das ganze Terrainzeugs was gerade im s2ext-Forum diskutiert wird.
Nova has written
Aber Achtung: Bitte keine seitenlangen Diskussionen über ein Script. Ihr könnt zu einem Script etwas sagen, aber bitte nicht allzu viel.
Hm... ist man es anders von mir gewohnt...?
Nova has written
Ebenso stehen alle Scripts in diesem Thread zur freien Verwendung aus. Ihr könnt sie verwenden und verändern wie ihr wollt, solange ihr sie nicht selbst weitergebt. Wer mit diesen Bestimmungen nicht einverstanden ist soll sein Script hier nicht posten.;)
Ich stimme den allgemeinen Geschäftsbedingungen zu.
Vorab: Das folgende Skript und die Abhandlung dazu beschäftigen sich mit der Erzeugung eines Berges auf einem flachen Terrain während der Laufzeit (also nicht im Editor). Dabei werden ein paar mathematische und numerische Aspekte angerissen und eine grobe Methode skizziert.
So... ich habe mal das Problem mit dem Terrain etwas genauer unter die Lupe genommen.
Alles in allem ist es etwas schwieriger als hier [im s2ext-Forum] anfangs dargestellt und man kommt um ein paar mathematische und numerische Grundlagen nicht drumherum. Mit etwas Gespür für Mathematik sollten aber die Grundkenntnisse aus der Schule zum Verständnis genügen.
Im Prinzip geht es darum, jeder x,z-Koordinate in der Terrainmatrix eine neue Höhe zuzuordnen. Nebenbei betragen die Abstände innerhalb der Terrainmatrix 64 und nicht 50. Etwas mathematischer und abstrakter formuliert ist also eine Funktion in zwei Variablen gesucht, welche eine zweidimensionale Fläche im dreidimensionalen Raum beschreibt und zwei Punkten eine Höhe zuordnet.
Zur Vereinfachung kann man erst einmal den eindimensionalen Fall betrachten. Bspw. beschreibt die Funktion f(x)=100-x^2 (eine an der x-Achse gespiegelte und um 100 Einheiten in y-Richtung verschobene Parabel) einen "Berg" im kartesischen Koordinatensystem.
Im zweidimensionalen Fall ist so eine Funktion ebenfalls leicht gefunden. Diese sollte außerdem noch zwei Anforderungen erfüllen: An den Randpunkten soll y=0 sein - der Rand unseres Berges. Und für x=z=0 nimmt sie gerade ihr Maximum an - die Spitze des Berges.
Offensichtlich wird das bspw. durch die Funktion f(x,z)=[r^2-(x^2+z^2)]*(y_max/r^2) realisiert, wie man leicht nachrechnet. Die Bedingung x^2+z^2=r^2 beschreibt anschaulich einen Kreis in der x-z-Ebene mit dem Radius r und für diesen Fall ist f(x,z)=0. Sind x=z=0 so nimmt f(x,z) gerade ihr Maximum bei y_max an.
Soweit zur Theorie. Auf mögliche Transformationen wie Strecken, Dehnen, Stauchen o.ä. will ich jetzt nicht weiter eingehen. Außerdem fehlt noch eine Zufallskomponente für die Variation, da so ein Berg ja nicht perfekt symmetrisch ist. Solche Spielereien kann man ggf. auch nachträglich noch einbauen.
Nun zu Implementierung. Wir kennen die Abstände in der Terrainmatrix, geben Höhe und Radius des Berges vor, und beschreiben ihn durch obige Funktion. Außerdem setzen wir zur Vereinfachung eine Ebene voraus, d.h. die Höhe der Karte sei überall gleich. Ebenso soll unser Berg erst einmal genau in der Mitte der Karte sitzen - die Translation kann man ebenfalls nachträglich noch einbauen.
Im Prinzip geht man nun alle Punkte in der Terrainmatrix durch und ändert entsprechend unserer Funktion dort die Höhe. Zwischen diesen Punkten wird bereits intern interpoliert, so dass wir uns darum nicht kümmern brauchen. Das Problem geschachtelter Schleifen wird einfach per Timer/Event umgangen und sollte auch kein Problem sein (ich habe mich erstmal für den Timer entschieden womit sich das "wachsen" des Berges beobachten lässt).
Außerdem wird hier teilweise mit recht hohen Zahlen operiert, weshalb man wegen evtl. auftretender Überlauf- oder Auslöschungsphänomenen wieder auf die Kondition der Funktion achten muss.
Folgendes Skript wäre ein guter Ansatz für eine Implementierung welcher allerdings hinsichtlich der obigen Probleme und Parameter auf jeden Fall noch optimiert werden müsste. Alles in allem ist die Laufzeit auch recht hoch, weshalb so ein Skript eher Spielerei und weniger für die Anwendung gedacht sein dürfte...
-> Editor -> leere Karte (64x64 oder so) -> komplett abflachen -> Info-Objekt mit folgendem Skript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
on:start {
	event "init";
}
on:init {
	//Höhe des Berges (Vielfaches von 64...!)
	$y_max=256.0;
	//Radius des Berges (Vielfaches von 64...!)
	$radius=256.0;
	//Starte bei Koordinate... x,z -> -256
	$x=-$radius;
	$z=-$radius;
	//Schrittweite... cnt -> 8
	$cnt=($radius/32);
	timer "self",500,$cnt,"loop_z";
	timer "self",(500*$cnt),1,"blubber";
}
on:blubber {
	terraintexture "generate";
}
on:loop_z {
//	$y_max=($radius*$radius);
	$x=-$radius;
	loop("count",$cnt) {
		//unsere Funktion...
//		$y=((($radius*$radius)-(($x*$x)+($z*$z)))*($y_max/($radius*$radius)));
		//...besser konditioniert...!
		$y=($y_max-(((($x*$x)+($z*$z))/$radius)*($y_max/$radius)));
		//an terrain-fkt. anpassen...
		$y=($y/32.0);
		//Senkpunkte werden ignoriert...
		if ((terrain($x,$z,0)-50)<=$y) { terrain $x,$z,2,$y; }
		//paar Infos für die Konsolenausgabe...
		$coord_x=terrain($x,$z,3);
		$coord_z=terrain($x,$z,4);
		echo "$x, $y, $z ($coord_x,$coord_z)";
		//nächste x-Koordinate in der Terrainmatrix
		$x+=64;
	}
	//nächste z-Koordinate in der Terrainmatrix
	$z+=64;
}
Noch ein paar weitere Ausführungen zum Thema Kondition von Algorithmen erpare ich euch jetzt (-> s2ext-Forum). Ob das Skript nützlich oder sinnvoll ist, müsst ihr beurteilen. Auf jeden Fall finde ich es recht interessant/amüsant.
Außerdem hatte ich mich neulich mit einem Platzierungsskript beschäftigt, welches ähnlich wie die Füllfunktion im Editor die Karte mit Vegetation füllt. Allerdings wird die Karte nicht 'vertikal' sondern 'horizontal' unterteilt, so dass bspw. von 'Norden nach Süden' (statt von 'oben nach unten') unterschiedliche Vegetationsgebiete erstellt werden welche zudem in einander übergehen. Das Skript wurde aber nur für den eigenen Bedarf geschrieben und müsste bei Interesse vielleicht noch etwas optimiert werden.
Ansonsten wären noch die Platzierungsskripte sehr nützlich, welche ich bei s2ext eingefügt hatte. Damit lassen sich bspw. beliebige Objekte kreisförmig anordnen. Radius, Anzahl... alles weitgehend parameterabhängig. Und selbst Info-Fähnchen lassen sich im Kreis platzieren - bspw. für geschmeidigere Kamerafahrten oder sowas. Ach, was solls, moment... (muss etwas kürzen)
PS-Straight
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//skip;
//Bei der Orientierung muss man auf charakteristische
//Merkmale eines Modells achten.
//Ansonsten hilft insbesondere bei symmetrischen
//Objekten auch die gebogene Palme als Orientierung.
//Zeigt sie nach Norden,dann werden auch die Objekte
//in Richtung Norden platziert (standard). Hier muss
//dann zusätzlich der Typ des entsprechenden Objekts
//angegeben werden (per $typ).
//Über $amount lässt sich die Anzahl der zu platzierten
//Objekte und per $dist der Abstand bestimmen.
//Beispiel: Wir wollen 10 Tannenbäume in eine
//bestimmte Richtung platzieren. Also setzen wir die
//Variable $amount auf 9, dazu noch $dist auf 200.
//Da die Tannenbäume recht symmetrisch sind, können
//wir kaum abschätzen, in welche Richtung sie
//letztendlich platziert werden. Also setzen wir
//zusätzlich $typ auf 21
//(Typ wird im Editor angezeigt, wenn man mit dem
//Mauszeiger über das zu platzierende
//Objekt geht) und wählen nun die gebogene Palme im
//Editor aus.
//Anschließend drehen wir die Palme in die gewünschte
//Richtung (Mausrad) und platzieren sie. Die Palme wird
//nun durch eine Tanne ersetzt, und in die Richtung
//in die sie zeigte, werden 9 weitere Tannen gesetzt.
//Weitaus sinnvoller wird das ganze aber bspw. bei den
//Palisadenwällen (Typ 218).
//Hier empfielt sich allerdings $dist auf 99 zu setzen,
//damit sie lückenlos aneinander stehen.
//Nun wollen wir mal 10 Häuser (Typ 194) in Reihe
//platzieren. Für $dist empfielt sich hier der Wert 185.
//Ein geschultes Auge kann hier im Editor bereits
//zwischen vorne und hinten unterscheiden, so dass
//wir die gebogene Palme als Orientierungshilfe nicht
//unbedingt benötigen. Allerdings werden die Häuser
//hintereinander, statt nebeneinander platziert. Um
//dieses Problem zu lösen, setzen wir $rotate1 auf 0.
//Nun werden die Häuser zwar nach rechts angereiht,
//allerdings sind sie selbst ausgerichtet. Setzen wir nun
//noch $rotate2 auf 0, dann ist alles korrekt. Die Häuser
//werden nach rechts aneinander gereiht, und sind nun
//nach vorne ausgerichtet.
//Und vergesst nie das Platzierungsskript nach getaner
//Arbeit wieder zu deaktivieren!
//Auch wenn sich Objekte meist einfacher löschen als
//setzen lassen, kann es unter Umständen auch mal
//fatale Folgen haben, wenn aus Versehen per
//Platzierungsskript 30 Felsen quer über die Insel
//gelegt werden...
$typ=type("self");
//INTIALISIERUNG
$class=0;
$typ=0;
$amount=1;
$dist=155;
$rotate1=90;
$rotate2=-90;
//konstante Höhe
$hold_y=0;
//erstes Objekt wieder löschen
$delete_first=0;
//erstes Objekt wurde im Editor platziert...
$x=getx("self");
$y=gety("self");
$z=getz("self");
//$yaw=(getyaw("self")+($rotate1+0.003));
$yaw=getyaw("self");
//aktuellen Objekttyp platzieren, falls nicht anders angegeben...
if ($class==0) {
	$class=currentclass();
}
if ($typ==0) {
	$typ=type("self");
	if ($delete_first==1) { free "self"; }
} else {
	$id=create($class,$typ,$x,$z);
	setrot $class,$id,"self",$yaw,"self";
	free "self";
	if ($delete_first==1) { free $class,$id; }
}
$yaw+=($rotate1+0.003);
//dazu kommen $amount Objekte
$x_dist=((sin($yaw-90.001)*$dist)/100.0);
$z_dist=((cos($yaw-90.001)*$dist)/100.0);
if ($amount>0) {
	loop("count",$amount) {
		$cnt=loop_id();
		$x+=$x_dist;
		$z-=$z_dist;
		$id=create($class,$typ,$x,$z);
		setrot $class,$id,"self",($yaw+$rotate2),"self";
		if ($hold_y==1) {
			setpos $class,$id,"self",$y,"self";
		}
		echo "id: $id x: $x z: $z yaw: $yaw";
	}
}
PS-Circle
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
//skip;
//Dieses Platzierungsskript realisiert die kreisförmige
//Platzierung von Objekten.
//Wir wollen bspw. 12 Blumen in einem Kreis
//positionieren. Also wählen wir im Editor
//ein paar Blümchen aus, laden dieses
//Platzierungsskript und klicken irgendwo auf
//die Karte. Easy.
//Jetzt wollen wir mal unser eigenes Stonehenge
//kreieren. Dazu wählen wir im Editor
//die Ruinen (Typ 141) und klicken irgendwo auf die
//Karte. Allerdings sind die Teile
//falsch ausgerichtet. Sie sollten alle um 90 Grad
//gedreht sein. Also setzen wir
//$rotate auf 90 und klicken wieder auf die Karte. Na,
//das sieht ja schon nicht schlecht aus... Allerdings
//könnte man vielleicht die Anzahl ($amount) etwas
//verringern, oder den Radius ($radius) vergrößern.
//Dann stehen die Teile nicht so dicht aneinander.
//So. Das war's auch schon. Beim Rest müsst ihr selber
//etwas herumexperimentieren...
//Na gut. Ein Tipp gebe ich noch. Wir wollen bspw. einen
//kreisförmigen Schutzwall aus Palisadenwällen (218)
//errichten. Zunächst ermitteln man empirisch per
//Platzierungsskript "ps_straight" einen optimalen
//Abstand von 99 ($dist).
//Bei sagen wir 32 Palisadenwällen hätte unser Kreis
//also einen Umfang von 99*32=3168. Dividiert durch
//2Pi kommt man dann auf einen Radius von etwa 504.
//Also setzen wir hier $radius=504, $amount=32 und
//zusätzlich $rotate=90 (für die korrekte Ausrichtung).
//Dann irgendwo auf die Karte klicken und wir erhalten
//einen (nahezu pefekt) kreisförmigen Palisadenwall. :)
//INITIALISIERUNG
$radius=100;
$amount=12;
$rotate=0;
$class=currentclass();
$typ=type("self");
//erstes Objekt wurde im Editor platziert...
$x_pos=getx("self");
$z_pos=getz("self");
//+1.0 für debug... o_O
$yaw=(getyaw("self")+1.0);
$angle=(360.0/$amount);
//dazu kommen $amount Objekte
if ($amount>0) {
	loop("count",$amount) {
		$cnt=loop_id();
		$yaw+=($angle);
		$x=$x_pos+(sin($yaw,0)*$radius);
		$z=$z_pos-(cos($yaw,0)*$radius);
		$id=create($class,$typ,$x,$z);
		echo "$cnt: $x, $z, $typ $id";
		setrot $class,$id,"self",($yaw-$rotate),"self";
//		setrot $class,$id,"self",($yaw+$rotate2),"self";
//		echo "id: $id x: $x z: $z yaw: $yaw";
	}
}
free "self";
Ah...! Ein tolles Skript habe ich noch! Es macht den Spieler unsterblich ^_^:
1
2
3
4
5
6
on:start {
	addstate "unit",1,17;
	loop("count",1000000000) {
		msg "Mc Leaf rult!";
	}
}
@Nova: Dein Skript lässt sich massiv vereinfachen und verkürzen. edited 2×, last 14.10.08 08:09:23 am