Zum Inhalt springen

YMAP-Builder

Bau eine Map-Placement-Datei, ohne einen Map-Editor zu öffnen: Koordinaten aus dem Spiel einfügen oder Props einzeln hinzufügen, Heading oder volle Rotation setzen und CodeWalker-fertiges .ymap XML mit berechneten Quaternions, Entity- und Streaming-Extents bekommen. Importier ein bestehendes ymap XML, um es zu bearbeiten.

Dieses Tool und seine Hinweise sind vorerst auf Englisch.

Config & CodeMaps, Props, DownloadZur Doku

Ymap

Entities (3)

  • 1
  • 2
  • 3

Import

Coordinates from GetEntityCoords(PlayerPedId()) are about 1 m above the ground. Set Z offset to for those, or use /ymappos from the helper below, which already snaps to the ground.

Top-down preview

-1405-1395-1385-1375-1365-1020-1010-1000prop_bench_01a -1386.72, -1004.23, 3.86 heading 125.0prop_bench_01a -1383.35, -1008.94, 3.86 heading 125.0prop_bin_07a -1385.10, -1006.62, 3.86 heading 125.0grid 5 m · N upGTA map

Click a dot to jump to its row, Shift or Ctrl click to select. The line shows the heading. Solid box: entity extents, dashed: streaming extents (the ymap loads while you are inside it).

No problems found.

Export

entities
3
flags
0
contentFlags
1
extent
23 x 25 m
fivemad_props.ymap.xml
<?xml version="1.0" encoding="UTF-8"?>
<CMapData>
 <name>fivemad_props</name>
 <parent />
 <flags value="0" />
 <contentFlags value="1" />
 <streamingExtentsMin x="-1496.72" y="-1118.94" z="-106.14" />
 <streamingExtentsMax x="-1273.35" y="-894.23" z="113.86" />
 <entitiesExtentsMin x="-1396.72" y="-1018.94" z="-6.14" />
 <entitiesExtentsMax x="-1373.35" y="-994.23" z="13.86" />
 <entities>
  <Item type="CEntityDef">
   <archetypeName>prop_bench_01a</archetypeName>
   <flags value="1572896" />
   <guid value="0" />
   <position x="-1386.72" y="-1004.23" z="3.86" />
   <rotation x="0" y="0" z="-0.8870108" w="0.4617486" />
   <scaleXY value="1" />
   <scaleZ value="1" />
   <parentIndex value="-1" />
   <lodDist value="100" />
   <childLodDist value="0" />
   <lodLevel>LODTYPES_DEPTH_ORPHANHD</lodLevel>
   <numChildren value="0" />
   <priorityLevel>PRI_REQUIRED</priorityLevel>
   <extensions />
   <ambientOcclusionMultiplier value="255" />
   <artificialAmbientOcclusion value="255" />
   <tintValue value="0" />
  </Item>
  <Item type="CEntityDef">
   <archetypeName>prop_bench_01a</archetypeName>
   <flags value="1572896" />
   <guid value="0" />
   <position x="-1383.35" y="-1008.94" z="3.86" />
   <rotation x="0" y="0" z="-0.8870108" w="0.4617486" />
   <scaleXY value="1" />
   <scaleZ value="1" />
   <parentIndex value="-1" />
   <lodDist value="100" />
   <childLodDist value="0" />
   <lodLevel>LODTYPES_DEPTH_ORPHANHD</lodLevel>
   <numChildren value="0" />
   <priorityLevel>PRI_REQUIRED</priorityLevel>
   <extensions />
   <ambientOcclusionMultiplier value="255" />
   <artificialAmbientOcclusion value="255" />
   <tintValue value="0" />
  </Item>
  <Item type="CEntityDef">
   <archetypeName>prop_bin_07a</archetypeName>
   <flags value="1572896" />
   <guid value="0" />
   <position x="-1385.1" y="-1006.62" z="3.86" />
   <rotation x="0" y="0" z="-0.8870108" w="0.4617486" />
   <scaleXY value="1" />
   <scaleZ value="1" />
   <parentIndex value="-1" />
   <lodDist value="100" />
   <childLodDist value="0" />
   <lodLevel>LODTYPES_DEPTH_ORPHANHD</lodLevel>
   <numChildren value="0" />
   <priorityLevel>PRI_REQUIRED</priorityLevel>
   <extensions />
   <ambientOcclusionMultiplier value="255" />
   <artificialAmbientOcclusion value="255" />
   <tintValue value="0" />
  </Item>
 </entities>
 <containerLods itemType="rage__fwContainerLodDef" />
 <boxOccluders itemType="BoxOccluder" />
 <occludeModels itemType="OccludeModel" />
 <physicsDictionaries />
 <instancedData>
  <ImapLink />
  <PropInstanceList itemType="rage__fwPropInstanceListDef" />
  <GrassInstanceList itemType="rage__fwGrassInstanceListDef" />
 </instancedData>
 <timeCycleModifiers itemType="CTimeCycleModifier" />
 <carGenerators itemType="CCarGen" />
 <LODLightsSOA>
  <direction itemType="FloatXYZ" />
  <falloff />
  <falloffExponent />
  <timeAndStateFlags />
  <hash />
  <coneInnerAngle />
  <coneOuterAngleOrCapExt />
  <coronaIntensity />
 </LODLightsSOA>
 <DistantLODLightsSOA>
  <position itemType="FloatXYZ" />
  <RGBI />
  <numStreetLights value="0" />
  <category value="0" />
 </DistantLODLightsSOA>
 <block>
  <version value="0" />
  <flags value="0" />
  <name>fivemad_props</name>
  <exportedBy>fivemad.com</exportedBy>
  <owner />
  <time />
 </block>
</CMapData>

This is CodeWalker XML. The game needs the binary .ymap: see the Convert tab.

Spawn it from Lua instead

The props in a ymap can also be created at runtime, so you can ship the same map as a plain resource with no .ymap in it and no CodeWalker step. The download below holds 3 props in a readable config.lua and a client.lua that spawns them around the player and deletes them again when you walk away.

Resource nameFolder and resource name. Empty uses fivemad_props_lua, so it cannot clash with the ymap resource.
total / at once

3 / 3

folder layout
fivemad_props_lua/
├── fxmanifest.lua
├── config.lua        <- the props
└── client.lua        <- spawns and deletes them

server.cfg:
ensure fivemad_props_lua

One prop, the short version

spawn_one_prop.lua
-- One prop, placed where the ymap would place it.
local model = `prop_bench_01a`
RequestModel(model)
while not HasModelLoaded(model) do Wait(0) end

-- CreateObjectNoOffset puts the entity origin exactly on
-- the coordinates, which is what a ymap stores. CreateObject
-- would lift it by the radius of the model.
-- isNetwork false: the prop is local to this client.
local prop = CreateObjectNoOffset(model, -1386.720, -1004.230, 3.860, false, false, false)

-- pitch, roll, yaw in degrees. Rotation order 2 is ROT_ZXY,
-- the one GetEntityRotation(entity, 2) reads back.
SetEntityRotation(prop, 0.00, 0.00, 125.00, 2, true)

-- The ymap's flag 32 (static entity): it cannot fall over.
FreezeEntityPosition(prop, true)
SetEntityLodDist(prop, 100)

-- The prop holds the model now, so let go of the request.
SetModelAsNoLongerNeeded(model)

-- Delete it on resource stop, or a restart leaves a second
-- copy standing:
--   SetEntityAsMissionEntity(prop, false, true)
--   DeleteObject(prop)
config.lua
-- fivemad_props_lua, generated by https://fivemad.com/tools/ymap-builder
-- 3 props from the ymap "fivemad_props", ready to spawn from Lua.
-- Every number here is the same one the .ymap export writes.

Config = {
    -- A prop is created once the player is this close, in metres, and removed
    -- again once he is spawnDistance + despawnMargin away. The margin is what
    -- stops a prop right on the line from flickering as you walk back and forth.
    spawnDistance = 150.0,
    despawnMargin = 25.0,

    -- How much one pass may do. Props come in a few at a time because every one
    -- of them takes a slot in the game's object pool (the client's own
    -- gameconfig.xml gives it 3300 entries, shared with every prop the map
    -- itself streams), and because a model may have to be loaded first.
    perTick       = 5,     -- props created in one pass
    checkInterval = 500,   -- ms between two distance checks
    maxActive     = 400,   -- never hold more than this many at once

    -- false: the props are there as soon as the resource starts.
    -- true:  nothing spawns until exports['fivemad_props_lua']:Load() is called. Same idea
    --        as the ymap "scripted" flag and RequestIpl.
    manual        = false,

    -- Give up on a model after this long instead of waiting forever. A model
    -- that does not exist never loads and never errors, it just never arrives.
    modelTimeout  = 10000,
}

-- One row per prop.
--   model        model name, or the raw hash when the ymap had no name for it
--   x, y, z      the entity origin, exactly as the ymap stores it
--   pitch, roll, yaw
--                the rotation in degrees the way GetEntityRotation(entity, 2)
--                reads it back (rotation order 2, ROT_ZXY)
--   lod          SetEntityLodDist in metres, -1 keeps the model's own distance
--   freeze       true for a static prop (ymap flag 32): no physics, cannot be
--                pushed over. false leaves the model's own physics on.
--   collision    only written when it is false (ymap flag 4)
--   tint         only written when it is not 0 (ymap tintValue)
Config.objects = {
    { model = 'prop_bench_01a', x = -1386.72, y = -1004.23, z = 3.86, pitch = 0.0, roll = 0.0, yaw = 125.0, lod = 100, freeze = true },
    { model = 'prop_bench_01a', x = -1383.35, y = -1008.94, z = 3.86, pitch = 0.0, roll = 0.0, yaw = 125.0, lod = 100, freeze = true },
    { model = 'prop_bin_07a', x = -1385.1, y = -1006.62, z = 3.86, pitch = 0.0, roll = 0.0, yaw = 125.0, lod = 100, freeze = true },
}

Which one belongs in your map

The ymap is the real map file. The engine loads it by its extents and draws it with the rest of the world: no script, no loop, proper lodDist and LOD chains, and the props are map entities rather than objects out of the client's object pool. That pool has 3,300 entries in the client's own gameconfig.xml and the map's own props are already in it. For anything that ships with your server, put fivemad_props.ymap in stream/.

The Lua spawner is a resource, not map data. It needs no CodeWalker and no reconnect: restart it and the props are there, which is what makes it good for testing, for events, and for props that have to come and go. The costs are real: a prop appears at your spawn radius instead of fading in, scale and ymap extensions are not reproduced, and every prop is a live object the game has to account for. The objects are not networked, so each client builds its own copy and the server has no handle on any of them.

Grab positions in game

Drop this into any client script on your dev server. Stand where a prop should go and run /ymappos, or look at an existing prop and run /ymapprop to copy its exact model, position and rotation. Then /ymapdump and paste the lines into Import. Every output line is a format the importer reads.

ymap_helper.lua
-- fivemad.com YMAP Builder helper (client side). Dev only, remove before going live.
-- /ymappos   your position on the ground + heading, as vector4
-- /ymapprop  model, position and rotation of the prop in front of the camera
-- /ymapdump  print every line collected so far (paste them all into the builder)
-- /ymapclear forget the collected lines
local lines = {}

local function out(line)
    lines[#lines + 1] = line
    print(line) -- F8 console, also saved in CitizenFX.log
    TriggerEvent('chat:addMessage', { args = { 'ymap', line } })
    -- lib.setClipboard(line) -- needs ox_lib
end

RegisterCommand('ymappos', function()
    local ped = PlayerPedId()
    local c = GetEntityCoords(ped)
    -- GetEntityCoords(ped) is about 1 m above the ground (pelvis), so snap to the ground
    local found, gz = GetGroundZFor_3dCoord(c.x, c.y, c.z + 0.5, false)
    local z = found and gz or (c.z - 1.0)
    out(('vector4(%.3f, %.3f, %.3f, %.2f)'):format(c.x, c.y, z, GetEntityHeading(ped)))
end, false)

local function camForward()
    local r = GetGameplayCamRot(2)
    local p, y = math.rad(r.x), math.rad(r.z)
    return vector3(-math.sin(y) * math.abs(math.cos(p)), math.cos(y) * math.abs(math.cos(p)), math.sin(p))
end

RegisterCommand('ymapprop', function()
    local from = GetGameplayCamCoord()
    local to = from + camForward() * 30.0
    -- 16 = objects. Map props only show up here if the game spawned them as objects.
    local ray = StartExpensiveSynchronousShapeTestLosProbe(from.x, from.y, from.z, to.x, to.y, to.z, 16, PlayerPedId(), 7)
    local _, hit, _, _, ent = GetShapeTestResult(ray)
    if hit ~= 1 or ent == 0 or GetEntityType(ent) ~= 3 then
        return print('ymap: no object in front of the camera')
    end
    local c, r = GetEntityCoords(ent), GetEntityRotation(ent, 2)
    -- the builder turns the model hash back into a name when it is a vanilla prop
    out(('model=%d pos=vector3(%.3f, %.3f, %.3f) rot=vector3(%.2f, %.2f, %.2f)'):format(GetEntityModel(ent), c.x, c.y, c.z, r.x, r.y, r.z))
end, false)

RegisterCommand('ymapdump', function()
    local all = table.concat(lines, '\n')
    print(all)
    -- lib.setClipboard(all) -- needs ox_lib
end, false)

RegisterCommand('ymapclear', function()
    lines = {}
    print('ymap: cleared')
end, false)

From in-game coords to a streamed ymap

  1. Add the helper from Grab positions in game to a client script on your dev server. Walk to each spot and run /ymappos, or look at an existing prop and run /ymapprop. Finish with /ymapdump.
  2. Paste the lines into Import, Coordinates, pick a default model and add them. Fine tune rows, or select several and bulk edit.
  3. Check the warnings, then download <name>.ymap.xml from Export.
  4. Convert it with CodeWalker: RPF Explorer, File, Open Folder on your stream folder, enable Edit mode, Edit, Import XML. You get <name>.ymap.
  5. Put it in stream/ of a resource with a plain fxmanifest.lua, ensure it and reconnect.

Everything runs in your browser and is saved locally, so you can come back to the same map later.

Or spawn the same props from Lua

The second export, Spawn it from Lua instead, builds a resource with no .ymap in it at all: aconfig.lua holding every prop and a client.lua that creates them around the player with CreateObjectNoOffset and deletes them again when you walk away. Same coordinates, same rotations, no CodeWalker step, and a restart is enough to see a change.

The two are not the same thing. A ymap is map data: the engine streams it by its extents, draws it with the rest of the world, honours lodDist and LOD chains, and costs no script time. Spawned props are real objects out of the client's object pool (3,300 entries in the gameconfig.xml FiveM ships, shared with every prop the map itself streams), they appear at your spawn radius instead of fading in, and scaleXY, scaleZ and entity extensions have no runtime equivalent at all. For a map that ships with your server, use the ymap. Use the Lua spawner for testing without CodeWalker, for events, and for props that have to come and go.

The generated script spawns nothing it does not have to: it measures the distance to each prop a few times a second, creates at most a handful per pass, keeps a ceiling on how many exist at once, and deletes everything on onResourceStop so a restart never leaves a second copy standing.

How rotation is stored

In game you think in heading (GetEntityHeading) or pitch, roll and yaw (GetEntityRotation(entity, 2)). Heading 0 faces north and 90 faces west. A ymap stores a quaternion instead, and it is the inverse of the in-game rotation. CodeWalker inverts it when it loads an entity and inverts it again when it saves. For a plain heading h:

rotation x = 0
rotation y = 0
rotation z = -sin(h / 2)
rotation w =  cos(h / 2)

heading 90  ->  z = -0.7071068, w = 0.7071068
heading 180 ->  z = -1,         w = 0

With pitch and roll the in-game rotation is Rz(yaw) * Rx(pitch) * Ry(roll) (rotation order 2, the default for GetEntityRotation), and the stored quaternion is its conjugate. Tilted entities also need flag 1 (allow full rotation), which this tool adds on export.

Heads up: CodeWalker's entity panel shows the stored quaternion as is, so its Euler Z reads minus your in-game heading. That is normal. Interior placements (CMloInstanceDef) are the one exception and store the rotation without inverting it.

Fields that matter

FieldWhat to use
flags32 freezes the prop (no physics). 1572864 are the two shadow bits almost every vanilla entity has. 1572896 is both. 0 leaves physics on for props that have it.
lodDistHow far away the prop is drawn, in metres. -1 uses the model's own value. 100 to 200 is fine for most props, go higher for big landmarks.
lodLevelLODTYPES_DEPTH_ORPHANHD for standalone props. HD, LOD and SLOD levels are for map LOD chains with parents.
priorityLevelPRI_REQUIRED always loads. The optional levels can be skipped by the game, so do not use them for anything gameplay related.
tintValueTexture variation of the prop (same as SetObjectTextureVariation).
extentsEntities extents box the props, streaming extents add the lodDist. The ymap is only loaded while you are inside the streaming extents. Both are filled in on export.

Common pitfalls

  • Props float 1 m in the air. Player coords are at the pelvis. Use /ymappos (ground snapped) or set Z offset to -1 when importing.
  • Props face the wrong way. Someone wrote the heading straight into the quaternion without inverting it. Import the XML here and the headings are read correctly.
  • Props vanish when you walk away. lodDist too low, or streaming extents too small because they were not recalculated after moving things.
  • Nothing shows up. Check the ymap is binary (not the .xml), the file name is unique across all resources, and the scripted flag is off (or you call RequestIpl).
  • Custom props are invisible. The ymap only places them. Their .ydr, .ytd and .ytyp have to be streamed as well.
  • Huge ymap covering half the map. The whole file streams in as soon as you enter its extents. Keep one ymap per area.
  • Changes do not show. Reconnect. Map files are loaded when you join, a resource restart is not always enough.

Do I need this_is_a_map?

No. A .ymap in stream/ is registered on its own. this_is_a_map 'yes' tells FiveM to reload the map store when the resource starts, which matters when you replace vanilla map files, not when you only add props.