• Welcome to Final Fantasy Hacktics. Please login or sign up.
 
March 28, 2024, 10:59:19 am

News:

Use of ePSXe before 2.0 is highly discouraged. Mednafen, RetroArch, and Duckstation are recommended for playing/testing, pSX is recommended for debugging.


How to create a custom map

Started by Kivutar, May 12, 2011, 10:17:56 am

Kivutar

May 12, 2011, 10:17:56 am Last Edit: April 27, 2012, 11:29:26 am by lirmont
This guide demonstrate how to create a custom map for tethical from scratch. Step by step.

Step 1: Modeling a flat map using Blender


  • Download, install Blender and launch it.

  • You will see a cube.

  • You can turn the camera dragging with the middle mouse bouton.

  • Press TAB to enter the Edit Mode for this cube.

  • Press A to unselect all the vertices.

  • Select the 4 upper vertices using the right mouse button.

  • Click on the blue arrow with the left mouse button.

  • Keep CTRL pressed to stick to the grid.

  • Lower the cube upper face of 1/2 unit. Press left mouse button again to stop.

  • Go to the View menu and click on Top. This will change your view and you will see the map from the top.

  • Press B to enter the box selection mode.

  • Select all the vertices of a side of the cube.

  • Press E to open the Extrude menu. Extrude the selected vertices on 1 unit.

  • Repeat the extrude step as shown on the video.

  • When done, press TAB to return to object mode.

  • Save your model in the client folder, under the models/maps folder.





Step 2: Giving a shape to your map


  • Reopen you map

  • Select you object

  • Enter edit mode by pressing TAB

  • Use the "face selection mode"

  • Select some faces, using the right mouse button

  • Move them, or extrude them, depending on the result you need (see the video) but remember to always stick to the grid using CTRL.

  • Return tu object mode using TAB

  • Save your file





Step 3: Apply a texture to your map


  • Your texture must have a power of 2 resolution (for instance, the one I use for this tutorial is 256x256)

  • Open your map with Blender

  • Select your object with the right click

  • Right click on the lower border, and split the screen verticaly

  • Open the UV editor on the second pane

  • Open your image in the UV editor

  • Select your object with the right click in the first pane

  • Enter edit mode for your object using TAB

  • Be sure to be in face selection mode if you are not (on the video I already am)

  • Select some faces

  • Press U to unwrap them

  • If your image is not selected in the UV editor, you can select it using the combobox

  • You should see your selected faces projected in the UV editor

  • In the first pane, select the Textured View Mode for your object, this allow you to see your modifications in live

  • Now you have to fix the place of your faces on the texture, for this, I use S to resize, G to grab, A to select/unselect, B to box-select

  • You can snap to pixel using the menu

  • Once done, return to object mode

  • You can see the textures are blur, you can fix that by disabling Mipmapping in blender. For this, drag the upper border, go to the OpenGL tab and disable Mipmapping

  • Save your file





Step 4: Export your map to Panda3D format


  • Download Chicken, the EGG exporter for Blender http://sourceforge.net/projects/chicken-export/

  • The installation of Chicken consist in unzipping the files in a folder, but the place of this folder differ depending of your OS, please refer to Blender documentation for your OS

  • Open you map with Blender

  • Select your object

  • Use File->Export->Chicken

  • The Chicken exporter should appear in one of your panes

  • Check 'use relative textures'

  • And click export, you should see a confirmation message

  • Your .egg map should appear in the same folder as your .blend map: client/models/maps/

  • If you have Panda installed, you can visualize your map with a tool provided by Panda

  • You can see on the video that our map is all blurry, this is due to wrong texture filters set by the exporter

  • You can fix it by openning the .egg with a simple text editor and change LINEAR to NEAREST

  • Your map is ready on the client side :)





Step 5: Adding your map server side

A map file on server side is a simple JSON file describing the map data:

{
    "name":  "custom001",
    "x":     9,
    "y":     9,
    "z":     5,
    "scale": [ 1.5, 1.5, 1.285714286 ],
    "offset": [ -4.5, -4.5, 0.0 ],
    "model": "custom001",
    "music": "10",
    "backgroundcolor1": [ 0.34, 0.24, 0.156, 1.0 ],
    "backgroundcolor2": [ 0.89, 0.734, 0.53, 1.0 ],
    "lights": [
        { "color": [ 0.8, 0.8, 0.8, 1.0 ] },
        { "color": [ 0.5, 0.5, 0.5, 1.0 ], "direction": [ 0.0, 45.0, -45.0 ] }
    ],
    "chartiles": {
        "1": [
               { "x": 2, "y": 0, "z": 0, "direction": 2 },
               { "x": 3, "y": 0, "z": 0, "direction": 2 },
               { "x": 4, "y": 0, "z": 0, "direction": 2 },
               { "x": 5, "y": 0, "z": 0, "direction": 2 }
             ],
        "2": [
               { "x": 2, "y": 8, "z": 2, "direction": 4 },
               { "x": 3, "y": 8, "z": 0, "direction": 4 },
               { "x": 4, "y": 8, "z": 0, "direction": 4 },
               { "x": 5, "y": 8, "z": 0, "direction": 4 }
             ]
    },
    "tiles": [
        { "x": 0, "y": 0, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 1, "y": 0, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 2, "y": 0, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 3, "y": 0, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 4, "y": 0, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 5, "y": 0, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 6, "y": 0, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 7, "y": 0, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 8, "y": 0, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
       
        { "x": 0, "y": 1, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 1, "y": 1, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 2, "y": 1, "z": 1, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "convex_sw" },
        { "x": 3, "y": 1, "z": 1, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "slant_s" },
        { "x": 4, "y": 1, "z": 1, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "convex_se" },
        { "x": 5, "y": 1, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 6, "y": 1, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 7, "y": 1, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 8, "y": 1, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
       
        { "x": 0, "y": 2, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 1, "y": 2, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 2, "y": 2, "z": 1, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "slant_w" },
        { "x": 3, "y": 2, "z": 2, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 4, "y": 2, "z": 1, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "slant_e" },
        { "x": 5, "y": 2, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 6, "y": 2, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 7, "y": 2, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 8, "y": 2, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
       
        { "x": 0, "y": 3, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 1, "y": 3, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 2, "y": 3, "z": 1, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "convex_nw" },
        { "x": 3, "y": 3, "z": 1, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "slant_n" },
        { "x": 4, "y": 3, "z": 1, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "convex_ne" },
        { "x": 5, "y": 3, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 6, "y": 3, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 7, "y": 3, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 8, "y": 3, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
       
        { "x": 0, "y": 4, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 1, "y": 4, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 2, "y": 4, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 3, "y": 4, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 4, "y": 4, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 5, "y": 4, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 6, "y": 4, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 7, "y": 4, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 8, "y": 4, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
       
        { "x": 0, "y": 5, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 1, "y": 5, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 2, "y": 5, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 3, "y": 5, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 4, "y": 5, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 5, "y": 5, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 6, "y": 5, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 7, "y": 5, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 8, "y": 5, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
       
        { "x": 0, "y": 6, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 1, "y": 6, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 2, "y": 6, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 3, "y": 6, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 4, "y": 6, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 5, "y": 6, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 6, "y": 6, "z": 4, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 7, "y": 6, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 8, "y": 6, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
       
        { "x": 0, "y": 7, "z": 2, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 1, "y": 7, "z": 2, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 2, "y": 7, "z": 2, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 3, "y": 7, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 4, "y": 7, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 5, "y": 7, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 6, "y": 7, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 7, "y": 7, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 8, "y": 7, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
       
        { "x": 0, "y": 8, "z": 2, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 1, "y": 8, "z": 2, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 2, "y": 8, "z": 2, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 3, "y": 8, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 4, "y": 8, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 5, "y": 8, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 6, "y": 8, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 7, "y": 8, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" },
        { "x": 8, "y": 8, "z": 0, "walkable": true, "selectable": true, "depth": 0.0, "scale": 1.0, "slope": "flat" }

    ]
}


name = the name of your map
x, y, z = dimentions of the map, ours is 9x9, the z is the height*4+1. I think i'm going to remove these properties since the server can compute them itself using the tile data. But for now, you need to set them.
scale = controls the scale of your map model, don't touch this, there is a scale for original maps, and a scale for custom maps.
offset = place the center of the map on the (0,0) tile, on custom maps, it is [ x/2, y/2, 0.0 ], as our map is 9x9, we end with [ -4.5, -4.5, 0.0 ]
model = the filename of your .egg client side
music = the filename of your .ogg client side. Maybe I'm going to remove this too, as music should be chosen by the party creator
backgroundcolor1, backgroundcolor1 = the gradient of the background. this is RGBA, so [1,1,1,1] is white, and [0,0,0,1] is black
lights = a list of lights, if no direction specified it is an ambiant light, if a direction is specified, it is a direction light. Direction coordinates are HPR (see this for more info)

chartiles = the team placements. "1" and "2" are the team ID. This is not very smart, I think I'm going to change it too. direction goes from 1 to 4 and is the initial direction of the character.

tiles = the big part:
Our map is 9x9, so x goes from 0 to 8, and y too.
Remember that a wooden box is 4 z.
walkable is self explanatory
selectable is not used yet
depth is usefull for water, try to set it to 2.0
scale is the z scale of a slope, default to 1.0, but can be 2.0
slope is the shape of your tile, here are the available slopes:

  • flat

  • slant_n

  • slant_s

  • slant_e

  • slant_w

  • convex_ne

  • convex_nw

  • convex_se

  • convex_sw

  • concave_ne

  • concave_nw

  • concave_se

  • concave_sw



Save your .json file under the server/maps directory.

When you launch your developement server, the server finds the map in the maps folder and add it to the list of available map.
Congratulations, your map is complete!

Tethical, an online FFT clone