Version 43 (modified by jo, 11 years ago) (diff) |
---|
How to create Orxonox Levels
It it recommended to first have a look at some of the existing levels. In the single player menu at the tab all to the right you find a complete list of all orxonox levels.
Create the basic file
- Go to the folder ../data/levels. The level files ending with .oxw are simple XML files and can be opened with any editor. Each level file stored in this folder can be selected later in Orxonox.
- Copy „empty_level.oxw“ and paste it again in the folder.
- Rename the copy. Try to use a name that sounds good. Avoid whitespace – use „_“ or CamelCase instead.
- Open your level with your favourite editor.
About XML
XML is a description language that looks similar to HTML. We use tags to describe objects:
<templates> <!-- Opening tag 1 --> <Template link=lodtemplate_default /> <!-- Opening and closing tag 2 at once --> </templates> <!-- Closing tag 1 -->
A <tag> can contain several attributes. An attribute is the place where a value is set.
<Light type=directional position="0,0,0" direction="0.253, 0.593, -0.765" diffuse="1.0, 0.9, 0.9, 1.0" specular="1.0, 0.9, 0.9, 1.0" /> <!-- Quite a lot of attributes. -->
The syntax is attribute=“value(s)“. If only one value is set, the quotation marks are not necessary.
Tip: XML files do not have to be compiled. If you changed a level file (and didn't forget to save it), you simply have to reload the level to view the changes. If your level doesn't load have a look at the terminal by pressing [alt] + [tab].
What it's all about
By describing a level via XML you actually add C++ objects to the level. Each tag starting with a capital letter refers to a class that contains a XMLPort function. In order to understand better what a certain tag is doing it is recommended to read the corresponding source files. Example: The <Light> tag shown before refers to the light class.
First steps
- Define the level's name in the menu. The description will be displayed in the level selection menu when a player hovers his mouse over your level's "name". screenshot should be a *.png image placed in the data_extern/images/levelpreviews folder. This should be the first tag in the XML file.
<LevelInfo name = "Teambase Match" description = "Fight for the bases." tags = "" screenshot = "teambase.png" />
- Decide wether you create a level for a specific gametype or a single player mission. The gametype is set in the <Level> tag.
<Level gametype = "TeamBaseMatch" >
- Set the level's backgroud (skybox). The skybox is an image of what you can seen on the horizon. You can add an new skybox by changing the corresponding parameter:
<Scene ambientlight = "0.5, 0.5, 0.5" skybox = "Orxonox/Starbox" >
Possible values for skybox are:
"Orxonox/Starbox" | "Orxonox/skypanoramagen1" | "Orxonox/skypanoramagen2" | "Orxonox/skyBoxMoreNebula" |
Basics
- Worldentity: Point in space with orientation ( ~point with a vector attached to it).
- Static Entity: Worldentity with a fixed position and fixed orientation.
- Movable Entity: Worldentity that can rotate or move constantly.
- Controllable Entity: Completely freely movable point. Usually steered by a controller.
- Model: Each visible 3d-Object. A model consists of a mesh (the form) and a material (the surface colouring).
- Collisionshape: A Collisionshape is the physical representation of a model. Currently collisionshapes only have the form of a sphere, cube/ashlar, cone or plain. For StaticEntities the collisionType is „static“. Movable Entities have the collisionType "dynamic". At the moment static collisionshapes do not provide a shield against bullets. The collision of projectiles is only detected by dynamic collisionshapes. Another significant difference: If you collide with a static collisionshape, you'll be pushed away. If you collide with a dynamic collisionshape you'll push the dynamic collisionshape and it's movable entity away.
Or even more vivid - if you place two movable entities with collisionshapes too close together, such that the collisionshapes ovelap, they'll burst apart. If both objects are static they will stay as they are, although their collisionshapes overlap.
Worldentity + Model + Collisionshape = adding objects to the level
<StaticEntity position="0,-10000,0" direction="0,-1,0" collisionType=static mass=100000 friction=0.01 > <attached> <Model position="0,0,0" mesh="cube.mesh" scale3D="10000,10000,1000" /> </attached> <collisionShapes> <BoxCollisionShape position="0,0,0" halfExtents="10000,10000,1000" /> </collisionShapes> </StaticEntity>
The StaticEntity defines the model's place and orientation (and some other values). The Model (a cube) is attached to the StaticEntity. With the proper sized collisionshape attached to the StaticEntity you have a "solid" cube. Without a collisionshape, the cube wouldn't be solid and your spaceship could just fly through it. This exampe is quite useful, since you usually can't see a collisionshape's size. If you combine an invisible collisionshape with a fitting model you can see where a collisionshape is for testing purposes. In this special case both the scale3D and the halfExtents parameters are identical.
MovableEntity - Let's get the world moving
Worldentities can be attached to other worldentities. If you want a model to move in circles, you can create a MovableEntity that rotates and a StaticEntity attached to it. The model that should be rotating is attached to the StaticEntity.
<?lua dofile("includes/CuboidSpaceStation.lua") ?> <!-- ----------------Rotating SpaceStation--------------- --> <MovableEntity position="1,1,1" rotationrate="-4.5" rotationaxis="0,0,1"> <attached> <StaticEntity position="-2500,0,0" yaw=90 pitch=90> <attached> <?lua createSpaceStationPar(0,2,1,2,1,4,1,50) ?> </attached> </StaticEntity> </attached> </MovableEntity>
Note that in this example the Model is created by a lua script that is called in a lua tag. Lua is a scripting language that is a very powerful tool for level design.
Models
A level depends on its models. All finished models are stored in ../data_extern/models. If you want to view most models in the gallery.oxw level. At the moment we have several asteroids, spaceships, a satellite, two different space stations and some smaller models.
Spawnpoints
A Spawnpoint is the entrance point for controllable entities (spaceships). Without a spawnpoint no level can work!
<SpawnPoint team=0 position="-200,0,0" lookat="0,0,0" spawnclass=SpaceShip pawndesign=spaceshipassff />
You can define which kind of spacecraft a player/ bots can use. Additionally the corresponding template has to be included:
pawndesign | include() | additional information |
spaceshipassff | "templates/spaceshipAssff.oxt" | default spaceship - equipped with rockets |
spaceshippirate | "templates/spaceshipPirate.oxt" | |
spaceshipswallow | "templates/spaceshipSwallow.oxt" | fast, nice design |
spaceshipHtwo | "templates/spaceshipH2.oxt" | |
spaceshipghost | "templates/spaceshipGhost.oxt" | stealth aircraft |
spaceshipHXY | "templates/spaceshipHXY.oxt" | |
spaceshipHXYSL | "templates/spaceshipHXYSL.oxt"|fast | |
spaceshipTransporterSL | "templates/spaceshipTransporterSL.oxt" | slow transporter |
spaceshipTransporter | "templates/spaceshipTransporter.oxt" | slow transporter, equal to SL version |
You find all spaceship files in ../data/levels/templates. The first entry in a file reveals the pawndesign:
<Template name=spaceshipghost> .
If the level is designed for several teams you have to use team spawn points.
<TeamSpawnPoint team=0 position="1000,0,0" lookat="0,0,0" spawnclass=SpaceShip pawndesign=spaceshipassff/>
Pickups
Pickups give a player a temporary bonus when collecting it. Bonuses are invisibility, health, boost, shield, a drone, …
- Include the pickups.
<?lua include("templates/pickupRepresentationTemplates.oxt") include("includes/pickups.oxi") ?>
- Add a PickupSpawner. An invisible device that puts pickups in the level.
<PickupSpawner position="-160,65,10" triggerDistance="10" respawnTime="5" maxSpawnedItems="10"> <pickup> <InvisiblePickup template=mediuminvisiblepickup /> </pickup> </PickupSpawner>
Pickupspawner - attributes:
- triggerDistance: Distance to collect the pickup. The larger the triggerDistance, the easier it is to get the pickup.
- respawnTime: After respawntime seconds a new pickup will appear, if the pickup had been collected.
- maxSpawnedItems: After maxSpawnedItems no further pickup will appear.
Pickups - have a look at pickups.oxw:
<ShieldPickup template=hugeshieldpickup /> | <ShieldPickup template=mediumshieldpickup /> | <ShieldPickup template=smallshieldpickup /> | |
<HealthPickup template=crazyhealthpickup /> | <HealthPickup template=hugehealthpickup /> | <HealthPickup template=mediumhealthpickup /> | <HealthPickup template=smallhealthpickup /> |
<SpeedPickup template=hugespeedpickup /> | <SpeedPickup template=mediumspeedpickup /> | <SpeedPickup template=mediumspeedpickup /> | <SpeedPickup template=smalljumppickup /> |
<InvisiblePickup template=hugeinvisiblepickup /> | <InvisiblePickup template=mediuminvisiblepickup /> | <InvisiblePickup template=smallinvisiblepickup /> | |
<MetaPickup metaType="use" /> | <MetaPickup metaType="drop" /> | <MetaPickup metaType="destroy" /> | <MetaPickup metaType="destroyCarrier" /> |
<DronePickup template=dronepickup /> | <PickupCollection template=triplehealthspeedinvisibilitypickup /> |
Billboards
Pickups are represented by billboards. Billboards are 2D images that are always facing the viewer. A 2D circle image seems to be a 3D sphere. E.g. The blinking lights on the assff wing are realized via billboards. Example of a static light sphere:
<MovableEntity position="0,0,0"> <attached> <Billboard position="100,0,0" material="Examples/Flare" colour="0, 0, 1" scale=1/> </attached> </MovableEntity>
Billboard in action:
- Mark points of interest with a light. Maybe this will attact the player.
Have a look at "theTimeMachine.oxw" to see some further billboards in action.
ForceFields
ForceFields push the player in a certain direction if the ForceField was triggered.
<ForceField position="-500,0,500" direction="0,0,-1" diameter=500 velocity=2500 length=750 />
As you maybe already have noticed - you don't have the ForceField to be attached to a Worldentity.
ForceField in action:
- Place the ForceField next to the Spawnpoint to give the player extra boost when entering the level.
Have a look at "asteroids.oxw" and "theTimeMachine.oxw", to see how to make ForceFields visible in the level.
Lua
Lua is the scripting language we use in our levels. At the beginning of the file templates for spaceships, the hud and more are loaded. What lua does in this context is editing the XML file and inserting the concerning XML (of the spaceships, hud, ..) before the level is loaded. That's why you cannot rely on the line numbers displayed when an error occurs, since before loading lua changes the file.
A quick overview over Lua: Learn Lua in 15 minutes
What lua can do:
- load external skripts: for example the cuboid spacestation.
<!-- First, the script has to be included (only once). --> <?lua dofile("includes/asteroidField.lua") ?> <!-- Usage: Creates an asteroid belt --> <?lua asteroidBelt(20000, 0, 13000, -48, -34, 70, 100, 200, 22000, 20000, 500, 1) ?> <!-- asteroidBelt(centerX, centerY, centerZ, yaw, pitch, segments, minSize, maxSize, radius0, radius1, count, fog) -->
- Create a bunch of objects (depending on the index i). Whatever is placed within those lua tags will be created several times.
Note that <?lua print(i*100 + 50) ?> directly inserts the calculated value when the level is loaded.
<!-- A for loop. The index i starts from 1, is increased up to 10 by adding +1. --> <?lua for i = 1, 10, 1 do ?> <SpawnPoint team=0 position="<?lua print(i*100 + 50) ?>,0,0" lookat="0,0,0" spawnclass=SpaceShip pawndesign=spaceshipassff /> <?lua end ?>
- Randomized values, sinus, cosinus and more. (Via sinus and cosinus circular shapes can be created easily. The randomization changes your level's appearance whenever it is reloaded.)
<?lua max = 16 for i = 0, max, 1 do y = math.sin(i/max*6)*750 z = math.cos(i/max*6)*750 ?> <StaticEntity position="<?lua print(y) ?>,0,<?lua print(z) ?>" scale="<?lua print(math.random() * 10 + 5) ?>" collisionType="static" > <?lua end ?>
Triggers && Events
Example: ../data/levels/events.oxw
So far your level design is static. Everything is placed in the level, when it is loaded (except the spawned objects) and doesn't change its behaviour once loaded. Via the trigger/event system you can make the level react on what the player does. A trigger is like a button. When you activate it, something happens.
<DistanceTrigger name="flying1" position="800,700,600" target="Pawn" distance=10 stayActive="true" delay=6/> <SimpleNotification message="Let's fly to the blinking light." broadcast="true"> <events> <trigger> <EventListener event="flying1" /> </trigger> </events> </SimpleNotification>
DistanceTriggers are activated, when the target (e.g. a pawn, a spaceship, …) is close enough to the distance trigger (defined via "distance"). The stayActive attribute keeps the switch activated once triggered - the event can only be created once. The name flying1 is needed in order to catch the event, when the trigger was activated. The SimpleNotification is sent to the player when the trigger is activated. Note that the trigger is activated with a delay of 6 seconds. The problem of distance triggers is that you have to rely on that the player is actually activating it as intended. Try to attract the player by using bliking billboards, pickups to reward the player, … and make the radius large enough such that the player can hardly miss it. Another problem might be that the distance trigger is triggered by another Pawn/Spaceship/… (whatever is specified as target). To exclude certain objects to activate the trigger or to only allow a specific object to activate the trigger, the DistanceTriggerBeacon was created.
<!-- This trigger CANNOT be triggered --> <DistanceTrigger position="10,-202, -42" distance="800" target="Spaceship" beaconMode="exclude" targetname="stupidSpaceship" name="dockMe"/> <!-- By this Spaceship --> <SpaceShip position = "10,-202, -42" ... > <attached> <DistanceTriggerBeacon name="stupidSpaceship" /> <Model mesh="HydroHarvester.mesh" mass=10 position="0,0,0" scale=50 /> </attached> <collisionShapes> <BoxCollisionShape position="-560,0,0" halfExtents="115,100,245" /> < ... > </collisionShapes> </SpaceShip>
<!-- This trigger can ONLY be triggered --> <DistanceTrigger position="10,-202, -42" distance="800" target="Spaceship" beaconMode="identify" targetname="coolSpaceship" name="dockMe"/> <!-- By this Spaceship --> <SpaceShip position = "10,-202, -42" ... > <attached> <DistanceTriggerBeacon name="coolSpaceship" /> <Model mesh="HydroHarvester.mesh" mass=10 position="0,0,0" scale=50 /> </attached> <collisionShapes> <BoxCollisionShape position="-560,0,0" halfExtents="115,100,245" /> < ... > </collisionShapes> </SpaceShip>
Quests
Example: ../data/levels/quests.oxw