Jump to content

Tutorial: World Objects


Recommended Posts

Creating and Manipulating World Objects

 

a22e8ad1ca02906f3309fc263b83bf3e.png

 

Seen questions pop a few times regarding placing objects on the map so decided to make a little tutorial on it.

 

Most of the tutorial is written in a single file included in a mod you can download below. The code is commented so you can see whats going on. I recommend loading the lua file in your favorite editor and starting a custom game without zombies with the mod activated in windowed mode so you can play with the mod ingame and read the stuff in the lua file.

 

How it works?

The mod allows you to place a red cube on a empty tile, every time you click that tile again the code will do some stuff with the object, you can all see it in the code comments.

 

Target audience.

The mod is written for beginning modders, however it might be that there's some stuff useful for more experienced modders here and there. You'll have to judge it yourself.

 

Code.

The code is written so that one can follow the tutorial line by line, as such it does not reflect proper implementation but rather serves as a showcase of things you can do and implement properly yourself. You can also build uppon the code supplied in the mod to use it as a testbase for experimenting with objects and their methods. I suggest also digging in TIS lua files regarding buildable objects and their context menu's.

 

About sprites.

To create a graphic for your object you'll need to make a transparent image of 64 width and 128 height. This covers one tile and one layer of height in the game.

Included in the mod are a few sprites, i suppose you could use "cube_floor.png" as a template:

17a68114c8d3521ee56a0617a263ed59.png

 

About java.

It's highly recommended you have some way of looking up java source classes & methods. The javadocs can be used, personally i've used JDGUI up until now.

 

With JDGUI you can open any class file in <root>/zombie/ directory and it opens up the entire project space as such:

2e63ae0774edfc1e9998ad5eb639e293.png

 

Many of these classes are made available in lua to work with. Often they require to be constructed first. To do so you can look up the constructors:

fe11bc445189a572f8749effb60154dd.png

In lua you'll have to use the 'new' method like so:

local myObj = IsoObject.new("parameters here");

By matching the set of parameters in .new() to the ones found in the java constructors you can call the appropriate one.

 

All other methods can then be called on that instance of the IsoObject like so:

myObj:setSprite("test");local sprite = myObj:getSprite();

The lua file for reference

--[[	World Object Tutorial	by TurboTuTone--]]-- table with names related to textures.-- full path: media/textures/cube_<spriteName>.pnglocal spriteNames = {	"green",	"red",	"blue",	"purple",	"yellow",	"plain",	"overlay",	"floor",}local function maskLoad( _path )	local tex = getTexture( _path );	tex:createMask( tex:getData() );	return tex;endCubes = {};Cubes.sprites = {};Cubes.LoadTextures = function()	for _,name in pairs(spriteNames) do		Cubes.sprites[name] = {};		Cubes.sprites[name].path = "media/textures/cube_" .. name .. ".png";		Cubes.sprites[name].tex = maskLoad( Cubes.sprites[name].path );	endend-- this function gets called with event: OnLoadedTileDefinitions-- this events triggers before the map data is loaded and supplies spritemanager which you have to use instead of getSprite.Cubes.LoadSprites = function( _sprMngr )	for _,t in pairs(Cubes.sprites) do		print(t.path);		t.sprite = _sprMngr:getSprite(t.path);	endend-- Main function of interest, linked to mouseup event.Cubes.doSquare = function( _x, _y )	-- convert mouse coordinates to world.	local mX, mY = ISCoordConversion.ToWorld(getMouseX(), getMouseY(), 0);	mX = math.floor( mX );	mY = math.floor( mY );		-- get the cell	local cell = getWorld():getCell();	-- get grid square (tile)	local sq = cell:getGridSquare(mX, mY, 0);	-- get list of objects on square	local objs 			= sq:getObjects();	-- get size of list	local objs_size 	= objs:size();		print("Objecs on square:", objs_size)	-- if there are no objects on the gridsquare (counts 1 for tile), place red cube.	if objs_size <= 1 then		--[[ RED CUBE --]]		print("Adding RedCube");		-- create instance of object with redquare sprite		local obj = IsoObject.new(cell, sq, Cubes.sprites.red.sprite);		-- set the objects name.		obj:setName("RedCube")		-- add object to square		sq:AddTileObject(obj);	else		-- if there are objects, see if we can find cubes to do stuff with.		for i=objs_size-1, 0, -1 do			if objs:get(i):getName() == "RedCube" then				--[[ GREEN CUBE --]]				-- we found red cube, lets remove it and place a green				objs:remove(i);				-- create new instance of isoobject with greenquare sprite				local obj = IsoObject.new(cell, sq, Cubes.sprites.green.sprite);				-- set the objects name				obj:setName("GreenCube");				-- add object to square				sq:AddTileObject(obj);				-- we are done, break				break;			elseif objs:get(i):getName() == "GreenCube" then				--[[ PURPLE CUBE --]]				-- found green cube, changing its sprite and name to purple				local obj = objs:get(i);				-- change sprite				obj:setSprite( Cubes.sprites.purple.sprite );				-- set the objects name				obj:setName("PurpleCube")				-- we are done, break				break;			elseif objs:get(i):getName() == "PurpleCube" then				--[[ RANDOM COLORED CUBE --]]				-- found purple cube, lets change texture to plain to play with tintmods				local obj = objs:get(i);				-- change sprite				obj:setSprite( Cubes.sprites.plain.sprite );				-- set the objects name				obj:setName("PlainCube");				-- the plain cube is a white schemed version so we can apply tintmod on the object to randomize its color.				-- make a tintmod				local customcol = ColorInfo.new( ZombRand(100)/100, ZombRand(100)/100, ZombRand(100)/100, 1.0 );				-- apply tintmod to object				obj:setCustomColor(customcol);				-- we are done, break				break;			elseif objs:get(i):getName() == "PlainCube" then				--[[ BLUE CUBE (with white dots overlay sprite) --]]				-- found plain cube, remove it				objs:remove(i);				-- adding blue cube				local obj = IsoObject.new(cell, sq, Cubes.sprites.blue.sprite);				-- set the objects name				obj:setName("BlueCube");				-- in this section we add a sprite to the objects childsprite list, the childsprites are drawn over the				-- object, first in the list gets drawn first etc. Ingame childsprites are for example smudges on walls.								-- get the childsprite arraylist, if none found create one				local childList = obj:getChildSprites() or ArrayList.new();				-- get a spriteinstance of the overlay sprite				local overlay = Cubes.sprites.overlay.sprite:newInstance();				-- add it to the childsprite list				childList:add( overlay );				-- set childsprites to object				obj:setChildSprites( childList );									-- add object to square				sq:AddTileObject(obj);					-- we are done, break				break;							elseif objs:get(i):getName() == "BlueCube" then				--[[ YELLOW CUBE --]]				-- found blue cube, remove it				objs:remove(i);								-- A thing you can do with sprites is set properties, for example we can make it function like a bed.				-- zombie/iso/SpriteDetails/IsoFlagType.class				-- try right clicking the yellow cube, youll notice you can use it to sleep.				-- get the yellow sprite (IsoSprite)				local yellow = Cubes.sprites.yellow.sprite;				-- get property container				local props = yellow:getProperties();				-- Set the bed flag				props:Set( IsoFlagType.bed );				-- set property container				yellow:setProperties( props );								-- creating yellow cube				-- until now the player has been able to pass through our object				-- we create it as IsoThumpable this time which allow us to use some additional functionallity.				local obj = IsoThumpable.new(cell, sq, Cubes.sprites.yellow.path, true, {});				-- set the objects name				obj:setName("YellowCube");				-- block all the square, i suspect this is to block it for pathfinding/item placement? not sure				obj:setBlockAllTheSquare(true);				-- make it unable to pass through				obj:setCanPassThrough(false);				-- NOTE: this object is added as special object, otherwise the block and passthrough settings wont work.				sq:AddSpecialObject(obj);								-- we are done, break				break;					elseif objs:get(i):getName() == "YellowCube" then				--[[ REMOVAL --]]				-- found yellow cube, remove procedure.				local obj = objs:get(i);				-- since last object was added as special object, remove it from objects & special objects list.				sq:getSpecialObjects():remove(obj);				sq:getObjects():remove(obj);				break;				--[[				That's it.				You could modify this piece of code to add more objects and/or play around with more stuff you find in the java.				It's allot of fun experimenting with objects and theres loads of stuff to be found not yet covered in this tutorial.				Good luck!								Important java classes to investigate:				- zombie/iso/IsoGridSquare.class				- zombie/iso/IsoObject.class				- zombie/iso/objects/*.class (all these extend IsoObject)				- zombie/iso/sprite/IsoSprite.class				- zombie/iso/SpriteDetails/IsoFlagType.class				- zombie/iso/SpriteDetails/IsoObjectType.class				--]]			end					end	endendCubes.GameStart = function()	Events.OnMouseUp.Add(Cubes.doSquare);endEvents.OnLoadedTileDefinitions.Add(Cubes.LoadSprites);Events.OnGameStart.Add( Cubes.GameStart );Events.OnGameBoot.Add( Cubes.LoadTextures );

 

 

 

Download

 

 

 

Regards,

Turbo

Link to comment
Share on other sites

  • 1 month later...

I am trying this tutorials method. The object is added, and detected. However it is not showing up graphically.

 

-----------------------------------------STACK TRACE-----------------------------------------Callframe at: getDatafunction: maskLoad -- file: SecureContainer.lua line # 20function: loadTextures -- file: SecureContainer.lua line # 38function: onLoadedTileDefinitions -- file: SecureContainer.lua line # 120java.lang.reflect.InvocationTargetException        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)        at java.lang.reflect.Method.invoke(Method.java:606)        at se.krka.kahlua.integration.expose.caller.MethodCaller.call(MethodCaller.java:61)        at se.krka.kahlua.integration.expose.LuaJavaInvoker.call(LuaJavaInvoker.java:199)        at se.krka.kahlua.integration.expose.LuaJavaInvoker.call(LuaJavaInvoker.java:189)        at se.krka.kahlua.vm.KahluaThread.callJava(KahluaThread.java:181)        at se.krka.kahlua.vm.KahluaThread.luaMainloop(KahluaThread.java:981)        at se.krka.kahlua.vm.KahluaThread.call(KahluaThread.java:162)        at se.krka.kahlua.vm.KahluaThread.pcall(KahluaThread.java:1721)        at se.krka.kahlua.vm.KahluaThread.pcallvoid(KahluaThread.java:1666)        at se.krka.kahlua.integration.LuaCaller.pcallvoid(LuaCaller.java:53)        at se.krka.kahlua.integration.LuaCaller.protectedCallVoid(LuaCaller.java:81)        at zombie.Lua.Event.trigger(Event.java:37)        at zombie.Lua.LuaEventManager.triggerEvent(LuaEventManager.java:83)        at zombie.iso.IsoWorld.init(IsoWorld.java:979)        at zombie.gameStates.GameLoadingState$1.run(GameLoadingState.java:237)        at java.lang.Thread.run(Thread.java:724)Caused by: java.lang.RuntimeException: No OpenGL context found in the current thread.        at org.lwjgl.opengl.GLContext.getCapabilities(GLContext.java:124)        at org.lwjgl.opengl.GL11.glGetTexImage(GL11.java:1637)        at zombie.core.textures.TextureID.getData(TextureID.java:325)        at zombie.core.textures.Texture.getData(Texture.java:705)        ... 19 more[Z cannot be cast to [Ljava.lang.Object;

Is the console output I see.

 *EDIT* Fixed. Was calling loadTextures in OnLoadedTileDefinitions instead of OnGameBoot

Link to comment
Share on other sites

Hehe yes that loadtextures function is a bit ugly, only used to set masks perhaps theres a better way to do this for single file sprites...

Anyhow, youre probably better of using texturepacks.

 

EasyP recently added a tut on how to set them up:

http://theindiestone.com/forums/index.php/topic/8790-custom-texture-packs-and-tile-definitions/

 

With a texture pack for example you could put all the cubes in one tilesheet, that automatically loads the masks properly and the bonus is that you can set some properties for the sprites in the editor which might come in handy if you need those.

 

instead of grabbing the sprites with:

getTexture( "media/textures/cube_red.png" );

with a texture pack you grab them like the game sprites, say if your sheet is called "cubes" its that plus "_indexnumber".

getTexture( "cubes_0" );

Regards,

Turbo

Link to comment
Share on other sites

Thanks! I saw that method of loading sprites earlier this morning when trying to make a build menu that fit in structure with the current lua build menus. Once I get more familiar with the innards of this game I'll definately branch out on the art front. For now Im just using the same texture everywhere :D

Link to comment
Share on other sites

  • 1 year later...

How can I spawn anything on a cell that is not loaded?

I try to spawn a crate, here is my test-code so far:

CLDmain = {};

CLDmain.spawnCrate = function()
	local mX, mY = ISCoordConversion.ToWorld(getMouseX(), getMouseY(), 0);
	mX = math.floor( mX );
	mY = math.floor( mY );
	--mX = 11795;
	--mY = 6671;
	
	-- get the cell
	local cell = getWorld():getCell();
	-- get grid square (tile)
	local sq = cell:getGridSquare(mX, mY, 0);

	-- get list of objects on square
	local objs 			= sq:getObjects();
	-- get size of list
	local objs_size 	= objs:size();
	
	print("Objecs on square:", objs_size)
	-- if there are no objects on the gridsquare (counts 1 for tile), place red cube.
	if objs_size <= 1 then
		-- create instance of object with redquare sprite
		local obj = IsoCrate.new(cell, sq, 8989);
		-- add object to square
		sq:AddSpecialTileObject(obj);
	end
end

CLDmain.GameStart = function()
	Events.OnMouseUp.Add(CLDmain.spawnCrate);
end

Events.OnGameStart.Add( CLDmain.GameStart );

It spawns something, but I can´t see it. Just notice it when I run around and suddenly can´t run any further (hitting the crate I suppose).

Link to comment
Share on other sites

Do you mean the exception in line 21 that get´s thrown after activating the mod? After restarting the game the mod works fine, just not very accurate with the mouse coords (cubes spawn anywhere near the player, not where you clicked).

 

I skimmed the class files some more and kinda found my first problem, with "getWorld():getCell()" I only get the current loaded cell. I didn´t find anything that let´s me get a specific cell (e.g. getCell(_x. _y)).

Hm, kinda lost right now.

 

What I also don´t quite understand is the last parameter from "IsoCrate.new((IsoCell cell, IsoGridSquare gridSquare, int gid)", I reckon the "gid" is a global ID, but how would I guess which gid is still available?

 

So many questions, so little knowledge :sad: 

Link to comment
Share on other sites

3 hours ago, Dr_Cox1911 said:

just not very accurate with the mouse coords (cubes spawn anywhere near the player, not where you clicked).

 

this is because it wasn't possible to zoom at the time. At a certain zoom level, the objects appear where they're supposed to. You'd have to factor in the current zoom level when getting the tile to fix this.

Link to comment
Share on other sites

I've been experimenting with this too and in mine changed it to only spawn the yellow bed block as I want to create sleeping bags, unfortunately I cannot figure out how to have this be an inventory item and be able to drop it and sleep on it. As well I do not like the line 21 error when exiting the game. It would be really nice if the devs would put together some tutorials on how to create a completely custom item and be able to assign it a property while using a custom image like in this old tut but without the errors. I'm looking at the camping tent code to see how it was done there but this is all very frustrating...

Edited by wintermuteai1
Link to comment
Share on other sites

On 16.3.2016 at 6:31 PM, ZombiesLoveBrainiacs said:

What's "8989"? Your sprite name is supposed to go in there - and you gotta load the sprite before using it.

I don´t think so. If you look at the constructor for the IsoCrate it has only one with just the cell in it and another one with a mysterious Integer named "gid". 8989 is just a random number, didn´t find a way to retrieve a valid gid (whatever that gid is). Or is a loaded sprite just a Integer (seems kinda strange, from my experience it should be some kind of pointer to the actual sprite/tex).

 

Just for clarification: I can spawn the blocks just fine, so the IsoObject is working, just can´t get the IsoCrate to show.

Link to comment
Share on other sites

  • 1 year later...
  • 7 months later...

Great tutorial!
Helped me dip my toes in and get started on my concept and idea!

 

Having a small issue though, or concern/wondering...

 

Creating my World Object works wonderfully - but it does not persist between world loads? is there any reason for this.

 

Second, the objs:remove(i) doesnt ever work for me

Code:

objs = sq:getObjects()
objs_size 	= objs:size();
for i=objs_size-1, 0, -1 do
	local _shaft = objs:get(i); --get the object itself.
	if(tostring(_shaft:getName()) == 'AMC_Transporter') --is it this kind of object?
    	objs:remove(i) --*** does not seem to work? ***
    else
    	--I do some logic, maybe manipulate the object - primarily just setting the Table (setTable())
    end
end
	
    

the logic is all sound, no errors, no issues, everything else works.

except the remove(i) on objs (again no errors).

 

Can anyone explain why?

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...