Home | A simple start. |
|
Introduction
This tutorial is reasonably complex; it helps if you've programmed in C++ or Java before.
23-08-2013: Updated; OnFrame() is now OnPreFrame().
The goal here is to create generic models for a car, and make them openable using keys. The example is a bit simple, in that it responds to keystrokes even when you're typing in the console. And it uses handles in a reasonably free way. Still, it might provide a good example of how to get things done in Onyx. Note that this tutorial was written with Racer v0.8.41; newer versions may still change interfaces, as it's a work in progress language.
The Car
The example car is Stereo's Simca Aronde; get it here. It contains a number of generic models; the trunk and doors are separated 3D models.
The Code
To get the code running, you need to load the Onyx script. In car.ini, set scripts.load to car.oxs and use the following script:
/* * Car script, handling any added logic * 08-06-2012: Created (based on Stereo's variant for the Simca Aronde in QScript) * NOTES: * - No constructors exist yet in Onyx; would be handy */ #include "racer.oxs" // Objects Car car; // The car we control // Angle animation class AngleAnimator { handle gm; // The generic model Vector3 pos, // Position of model rotMask; // Mask to define rotation (i.e. 0,1,0) int open; // Desired state float angle; // Current angle void UpdateAngle(float incRate,float decRate,float minAngle,float maxAngle,float interval) // Angle handler for a generic model. // // targetState: usually 0 (closed) to 1 (open), where 0 represents the smaller angle. // incRate: angular change (per ms) in radians when opening. // decRate: angular change (per ms) in radians when closing. note: both should be positive. // minAngle: min value for the angle // maxAngle: max value for the angle // 'interval' is the delta time since the last frame // // Returns the updated angle { float targetState=open; // Figure out where we want to go float targetAngle=targetState*(maxAngle-minAngle)+minAngle; // Move towards target angle if(targetAngle>angle) angle=angle+incRate*interval; if(targetAngle<angle) angle=angle-decRate*interval; // Clamp angle within a valid range if(angle<minAngle) angle=minAngle; else if(angle>maxAngle)angle=maxAngle; // display the object correctly GenModSetPosition(gm,pos); GenModSetRotation(gm,angle*rotMask.x,angle*rotMask.y,angle*rotMask.z); } }; AngleAnimator trunkAnim,leftAnim,rightAnim; // Generic int keyPressed; // Frame interval float lastT; void OnInit(handle h) // Called when the car is created // 'h' is the handle to the car { echo("OnInit of car"); // This next line should change in the future car.h=h; // Trunk animation setup trunkAnim.gm=car.GetGenericModel(8); trunkAnim.pos.x=0; trunkAnim.pos.y=0.35; trunkAnim.pos.z=-1.31; trunkAnim.rotMask.x=0; trunkAnim.rotMask.y=1; trunkAnim.rotMask.z=0; // Doors leftAnim.gm=car.GetGenericModel(9); leftAnim.pos.x=0.739; leftAnim.pos.y=-0.58; leftAnim.pos.z=0.628; leftAnim.rotMask.x=-1; leftAnim.rotMask.y=0; leftAnim.rotMask.z=0; rightAnim.gm=car.GetGenericModel(10); rightAnim.pos.x=-0.739; rightAnim.pos.y=-0.58; rightAnim.pos.z=0.628; rightAnim.rotMask.x=1; rightAnim.rotMask.y=0; rightAnim.rotMask.z=0; } void OnPreFrame() { float t=GetSimTime(); // Milliseconds t=t*0.001; // Time in seconds // Time since last frame float dt=t-lastT; lastT=t; if(IsKeyPressed(KEY_T)) { if(keyPressed==false) { echo("T pressed! Toggle trunk"); keyPressed=true; trunkAnim.open=1-trunkAnim.open; } } else if(IsKeyPressed(37)) { if(keyPressed==false) { echo("Left pressed! Toggle door"); keyPressed=true; leftAnim.open=1-leftAnim.open; } } else if(IsKeyPressed(39)) { if(keyPressed==false) { keyPressed=true; rightAnim.open=1-rightAnim.open; } } else { keyPressed=false; } // Animate trunkAnim.UpdateAngle(3.0,3.0, 0,1.2, dt); leftAnim.UpdateAngle(2.0,2.0, 0,1.1, dt); rightAnim.UpdateAngle(2.0,2.0, 0,1.0, dt); }
(last updated August 23, 2013 )