Model viewer

Dieses Tool wird es Ihnen erlauben, Ihre Models in all ihrer Pracht zu sehen. Kopieren Sie die Model-Dateien in dasselbe Verzeichnis wie den Viewer, geben Sie ihren Namen ein, wählen Sie Start und End-Frame, setzen Sie den Ambient und die Geschwindigkeit der Animation und genießen Sie den Anblick!

Zunächst einige String Definitionen:

string modelname_str = " "; // 20 chars, stores the model name
string start_str = " "; // holds the start frame
string end_str = " "; // holds the end frame
string empty_str = ""; // an empty string

Das Hauptpanel enthält 2 Slider, welche die Animationsgeschwindigkeit und den Ambientwert regulieren:

panel main_pan
{
bmap = main_pcx;
pos_x = 824;
pos_y = 0;
layer = 10;
vslider = 35, 340, 200, slider_pcx, 1, 50, frame_speed;
vslider = 133, 340, 200, slider_pcx, 0, 100, model_ambient;

digits = 95, 155, 3, arial_font, 1, frame_speed;
digits = 85, 210, 4, arial_font, 1, player.frame;

on_click input_data;
flags = refresh, d3d, visible;
}

Wenn wir auf dieses Panel klicken, wird die Funktion input_data() aufgerufen. Wir verwenden 3 Texte, um den Modelnamen zu speichern und anzuzeigen, ebenso wie den Start- und Endframe der Animation:

text modelname_txt
{
layer = 30;
pos_x = 857;
pos_y = 628;
font = arial_font;
string = modelname_str;
}

text start_txt
{
layer = 30;
pos_x = 900;
pos_y = 45;
font = arial_font;
string = start_str;
}

text end_txt
{
layer = 30;
pos_x = 900;
pos_y = 100;
font = arial_font;
string = end_str;
}

Werfen wir einen Blick auf die main Funktion:

function main()
{
level_load (mview_wmb);
wait (2);
camera.x = -150;
camera.y = 0;
camera.z = 30;
clip_size = 0; // show all the triangles for all the models
on_d = null; // disable the debug panel (key "D")
fps_max = 40; // lock the frame rate to 40 fps
mouse_init();
while (1)
{
if ((mouse_pos.x < 824) || (mouse_mode == 0)) // if the pointer isn't placed over the panel
{
camera.x += 10 * cos(camera.pan) * (mouse_right - mouse_left) * time;
camera.y += 10 * sin(camera.pan) * (mouse_right - mouse_left) * time;
camera.z += 30 * sin(camera.tilt) * (mouse_right - mouse_left) * time;
camera.pan -= 5 * mouse_force.x * time;
camera.tilt += 3 * mouse_force.y * time;
}
wait (1);
}
}

Wir laden das Level (ein ausgehöhlter Würfel), warten bis es geladen ist und setzen die Kameraposition hinter das Model. Wir stellen sicher, dass wir auch alle Polygone des Models sehen, schalten das Debugpanel ab und setzen die Framerate auf 40 fps fest.

Wenn der Mauszeiger nicht über dem Main Panel ist, wird er benutzt, um die Kamera mit Hilfe der zwei Maustasten zu bewegen. Wenn Sie sich die Main Funktion genau ansehen, werden Sie bemerkt haben, dass ich "vergaß", über die mouse_init() Funktion zu reden. Nun, hier ist der Code für diese:

function mouse_init()
{
mouse_map = cursor_pcx;
mouse_mode = 2;
while (1)
{
if (key_space == 1) {mouse_mode = 0;}
else {mouse_mode = 2;}
mouse_pos.x = pointer.x;
mouse_pos.y = pointer.y;
wait (1);
}
}

Diese Funktion stellt die Bitmap für den Mauszeiger ein, macht ihn sichtbar und bewegt ihn bei Bedarf über den Bildschirm. Falls die Leertaste gedrückt und gehalten wird, verschwindet der Mauszeiger und wir können uns in dem Würfel bewegen, um eine bessere Sicht auf unser Model zu bekommen.

Erinnern Sie sich an das Hauptpanel? Schauen Sie sich das Bitmap an: main.pcx. Wenn wir darauf klicken, läuft folgende Funktion:

function input_data()
{
if ((mouse_pos.x > 855) && (mouse_pos.x < 985) && (mouse_pos.y > 630) && (mouse_pos.y < 650))
{
modelname_txt.visible = on;
str_cpy (modelname_str, empty_str);
while (str_cmpi (modelname_str, empty_str) == 1)
{
inkey modelname_str;
wait (1);
}
str_cat(modelname_str, ".mdl");
if (file_open_read(modelname_str) == 0)
{
beep; beep;
}
else // the model exists
{
if (player != null)
{
ent_remove (player);
}
ent_create(modelname_str, nullvector, model_driver);
}
}

Wenn wir in die Region der "Model Name" Text Box geklickt haben, wird modelname_txt sichtbar gemacht und der String wird gelöscht. Die Schleife stellt sicher, dass wir einen Namen für das Model eingetippt haben; dieser Name ist in modelname_str gespeichert. Sie müssen nicht "guard.mdl" schreiben, denn der Code fügt automatisch die Endung .mdl an ihre Eingabe an, schreiben Sie also einfach "guard", "warlock" usw. (ohne die Anführungszeichen) für die Models.

Falls das Model nicht existiert (wenn der Name falsch geschrieben wurde oder die Datei sich nicht im gleichen Verzeichnis befindet, wie der Viewer), wird die Engine als Fehlermeldung zweimal piepen (beep). Falls das Model im Verzeichnis liegt und der Name korrekt geschrieben wurde, entfernen wir das zuvor geladene Model (falls es eines gab) und erstellen das Neue im Zentrum, wobei wir ihm die model_driver() Funktion als Action geben - mehr über die Funktion gleich.

if ((mouse_pos.x > 895) && (mouse_pos.x < 955) && (mouse_pos.y > 45) && (mouse_pos.y < 65))
{
start_txt.visible = on; // show the text
str_cpy (start_str, empty_str);
while (str_cmpi (start_str, empty_str) == 1) // make sure that the player has typed something
{
inkey start_str; // show the cursor -> store the input in start_str
wait (1);
}
start_frame = str_to_num(start_str);
}
if ((mouse_pos.x > 895) && (mouse_pos.x < 955) && (mouse_pos.y > 100) && (mouse_pos.y < 120))
{
end_txt.visible = on; // show the text
str_cpy (end_str, empty_str);
while (str_cmpi (end_str, empty_str) == 1) // make sure that the player has typed something
{
inkey end_str; // show the cursor -> store the input in start_str
wait (1);
}
end_frame = str_to_num(end_str);
}
}

Wenn in die Nähe der "Start Frame" oder "End Frame" Boxen geklickt wird, zeigen wir den entsprechenden Text und speichern die Eingabe in start_str bzw. end_str. Nun zurück zu model_driver():

function model_driver()
{
my.albedo = 0;
while (1)
{
camera.ambient = model_ambient;
my.frame += 0.03 * frame_speed * time;
if (my.frame > end_frame)
{
my.next_frame = start_frame;
}
else
{
my.next_frame = 0;
}
if (my.frame >= end_frame + 1)
{
my.frame -= (end_frame - start_frame + 1);
}
wait (1);
}
}

Diese Funktion gehört zu dem Model, das wir betrachten. Ich habe albedo auf 0 gesetzt, damit das Model so klar wie möglich wirkt; camera.ambient wird auf model.ambient gesetzt. Der Ambient und die Geschwindigkeit der Animation werden von den 2 Slidern auf dem main_pan gesetzt, erinnern Sie sich?

Das Model zirkelt zwischen dem von uns gesetzten Start und Endframe und benutzt dabei weiche Interpolation. Hier ist ein Zahlenbeispiel; es animiert ein Model mit start_frame == 8 und end_frame == 11.

if (my.frame > 11)
{
my.next_frame = 8;
}
else
{
my.next_frame = 0;
}
if (my.frame >= 12)
{
my.frame -= 4;
}

Zwei Models habe ich beigefügt: guard und warlock.

Taschenrecher

Mit Hilfe dieses Taschenrechners können Sie addieren, subtrahieren, multiplizieren und dividieren. Ich habe Code hinzugefügt, der mit Überläufen zurechtkommt (als Beispiel), aber Sie können den Taschenrechner verbessern, indem Sie Code schreiben, der Divisionen durch 0 abfängt, mit mehreren Operationen klarkommt (6 * 3 - 4 = 14 anstelle von 6 * 3 = 18, 18 - 4 = 14) usw.

Wir beginnen mit einigen String Definitionen:

string result_string = " "; // for 5 digits
string temp_string = " "; // for 2 digits, number of the button (1..15)

Die Main Funktion setzt die Hintergrundfarbe auf Schwarz (rgb = 000), den Mauszeiger und kopiert "0" auf das Display, denn der Taschenrechner ist gerade eingeschaltet worden, dann erlaubt sie uns, die Maus zu bewegen.

function main()
{
screen_color.red = 0;
screen_color.green = 0;
screen_color.blue = 0;
mouse_map = mouse_pcx;
mouse_mode = 2;
str_cpy(result_string, "0"); // start with a zero displayed on the screen
while (1)
{
mouse_pos.x = pointer.x;
mouse_pos.y = pointer.y;
wait (1);
}
}

Wir benutzen ein Panel für alle Knöpfe und ein Text für das Display:

panel main_pan
{
bmap = main_pcx;
pos_x = 0;
pos_y = 0;
layer = 10;

button = 292, 180, button1_pcx, button1_pcx, button1_pcx, null, key_pressed, null;
button = 332, 180, button2_pcx, button2_pcx, button2_pcx, null, key_pressed, null;
button = 372, 180, button3_pcx, button3_pcx, button3_pcx, null, key_pressed, null;
button = 292, 215, button4_pcx, button4_pcx, button4_pcx, null, key_pressed, null;
button = 332, 215, button5_pcx, button5_pcx, button5_pcx, null, key_pressed, null;
button = 372, 215, button6_pcx, button6_pcx, button6_pcx, null, key_pressed, null;
button = 292, 250, button7_pcx, button7_pcx, button7_pcx, null, key_pressed, null;
button = 332, 250, button8_pcx, button8_pcx, button8_pcx, null, key_pressed, null;
button = 372, 250, button9_pcx, button9_pcx, button9_pcx, null, key_pressed, null;
button = 292, 285, button0_pcx, button0_pcx, button0_pcx, null, key_pressed, null;
button = 332, 285, buttonplus_pcx, buttonplus_pcx, buttonplus_pcx, null, key_pressed, null;
button = 372, 285, buttonminus_pcx, buttonminus_pcx, buttonminus_pcx, null, key_pressed, null;
button = 292, 320, buttonmultiply_pcx, buttonmultiply_pcx, buttonmultiply_pcx, null, key_pressed, null;
button = 332, 320, buttondivide_pcx, buttondivide_pcx, buttondivide_pcx, null, key_pressed, null;
button = 372, 320, buttonequal_pcx, buttonequal_pcx, buttonequal_pcx, null, key_pressed, null;

flags = refresh, d3d, visible;
}
text display_text
{
layer = 20;
pos_x = 380;
pos_y = 145;
font = lcd_font;
string = result_string;
flags = visible;
}

Die Paneldefinition enthält 15 Knöpfe, die auf dem Bildschirm erscheinen. Wird einer von ihnen angeklickt, so wird die Funktion key_pressed aufgerufen und key_number wird auf einen Wert zwischen 1 (button1.pcx) und 15 (buttonequal.pcx) gesetzt, je nachdem, welcher Knopf gedrückt wurde. Sehen wir uns diese Funktion key_pressed mal genauer an:

function key_pressed(key_number)
{
snd_play(key_wav, 70, 0);
if (key_number >= 1 && key_number <= 10)
{
str_for_num(temp_string, key_number);
if (str_len(temp_string) == 2)
{
str_clip(temp_string, 1);
}
if (str_cmp(result_string, "0"))
{
str_cpy(result_string, temp_string);
}
else // we add a digit to the existing number
{
if (str_len(result_string) < 5)
{
str_cat(result_string, temp_string);
align_result();
}
}
}

Jedes Mal, wenn ein Knopf gedrückt wird, wird ein Sound abgespielt. Falls eine der Ziffern 1, 2, ..., 9 bzw. 0 gedrückt wurde, konvertieren wir die entsprechende key_number in einen String. Natürlich müssen wir etwas tun, wenn der zehnte Knopf gedrückt wird, denn er entspricht der 0, also ändern wir 10 auf den korrekten Wert. Das ist ganz einfach, denn 10 hat als einziges zwei Stellen, also entfernen wir mit str_clip das erste Zeichen und erhalten 0.

Falls das Display "0" anzeigt und ein Knopf gedrückt wurde, wird das Display durch den String ersetzt. Zeigt das Display irgendetwas anderes als 0, so wird die Ziffer einfach an die Zahl angehängt. Verstanden? Wenn Sie es in Aktion sehen, werden Sie merken, was ich meine. Ich habe die Länge der Zahlen auf 5 Ziffern begrenzt. Jedes Mal, wenn eine Ziffer hinzugefügt wird, gleicht die Funktion align_result() das Ergebnis von der Länge her an. Darüber sprechen wir nachher.

if (key_number >= 11 && key_number <= 14)
{
display_text.pos_x = 380; // restore the initial pos_x
str_cpy(result_string, "0");
number_one = str_to_num(result_string);
add = (key_number == 11);
substract = (key_number == 12);
multiply = (key_number == 13);
divide = (key_number == 14);
}

Wenn "+", "-", "x" oder ":" gedrückt wird, wird display_text an seine Startposition zurückgeschoben, denn das Display wird wieder "0" anzeigen. Die Zahl, die vorher auf dem Display stand, wird in number_one gespeichert; die zweite Zahl, die auf dem Display steht, bevor "=" gedrückt wird, wird in number_two gespeichert. Wenn einer der Knöpfe gedrückt wird, fügen wir Ziffer um Ziffer an das Display an. In Wahrheit fügen wir nur Elemente zu dem result_string hinzu, und nun ist es an der Zeit diesen in eine Zahl zu konvertieren, nämlich in number_one.

Falls auf "+" gedrückt wurde, dann wird add auf 1 gesetzt und substract, multiply und divide alle auf 0.

if (key_number == 15) // we have pressed "="
{
number_two = str_to_num(result_string);
result = (number_one + number_two) * add + (number_one - number_two) * substract + (number_one * number_two) * multiply + (number_one / number_two) * divide;
if (result < 99999)
{
str_for_num(result_string, result);
}
else // overflow
{
str_cpy(result_string, "ERROR");
}
align_result();
number_one = 0;
number_two = 0;
add = 0;
substract = 0;
multiply = 0;
divide = 0;
while (key_any == 0) {wait (1);} // don't erase the result - wait for the next mouse click
str_cpy(result_string, "0"); // reset the display
display_text.pos_x = 380; // restore the initial pos_x
}
}

Wird "=" gedrückt, dann wird result_string (das Display) in eine Zahl konvertiert und in number_two gespeichert. Die richtige Mathematik wird nun von einer simplen Formel erledigt:

result = (number_one + number_two) * add + (number_one - number_two) * substract + (number_one * number_two) * multiply + (number_one / number_two) * divide;

Naja, die Formel ist simpel, denn wenn wir zwei Zahlen addieren wollen, ist add = 1 und die anderen sind 0, also sieht die Formel so aus:

result = number_one + number_two

Das sieht doch schon besser aus, oder? Dasselbe geschieht, wenn "-", "x" oder ":" gewählt wird.

Falls das Ergebnis kleiner ist als 99999 (5 Ziffern, richtig?), wird die Zahl in den result_string konvertiert und auf dem Display gezeigt. Ist die Zahl größer, zeigt das Display "ERROR" an. Das Ergebnis wird mit der mysteriösen align_result() Funktion angepaßt und number_one und number_two etc. werden zurückgesetzt. Solange keine Taste gedrückt wurde, wird das Display zurückgesetzt und result_string wird zur Ursprungsposition zurückgeschoben.

Aber nun halte ich es nicht mehr aus! Ich muß Ihnen diese Funktion jetzt zeigen:

function align_result()
{
display_text.pos_x = 380 - (str_len(result_string) - 1) * 18; // move / align the text to the left as more digits appear / disappear
}

Sie ist simpel, aber sie funktioniert: sie gleicht den Text linksbündig an, wenn neue Zahlen erscheinen oder verschwinden. Ich weiß, dass ein Zeichen eine Breite von 18 Pixeln hat (schauen Sie auf die lcd_font Definition, falls Sie mir nicht glauben), also ziehe ich 18 * (number_of_digits - 1) von 380 Pixeln ab, um den Text nach links oder rechts zu verschieben.

Bitte bedenken Sie, dass dieser Taschenrechner mehr Schutz vor Fehlern braucht, z.B. bei Division durch 0 und so weiter. Momentan müssen Operationen wir folgt ausgeführt werden:

32 x 2 = (die 64 wird angezeigt) und dann müssen Sie 64 + 7 = (die 71 wird angezeigt) nochmal eingeben.

Sie können nicht einfach 32 x 2 + 7 = ... tippen, aber wenn Sie den Code verbessern möchten, können Sie einfach number_one und number_two durch einen Array ersetzen und die temporären Zahlen dort sichern.