Создание новой сущности

Материал из CryWiki Russia

(Различия между версиями)
Перейти к: навигация, поиск
(Новая страница: «<!-- Please enter the information about your article for each section after the equal sign. The information will be translated into a formatted page upon b...»)
Строка 16: Строка 16:
<!-- The article's topic (Basic Level Design, Terrain...)-->
<!-- The article's topic (Basic Level Design, Terrain...)-->
-
}}
+
 
= Creating a new entity =
= Creating a new entity =
In this document we will go through creating a new entity (in Lua), with support for flowgraphs.
In this document we will go through creating a new entity (in Lua), with support for flowgraphs.

Версия 12:20, 17 января 2012

О статье
Авторins
ПереводчикSodiet
СложностьСредняя
ТребованияТекстовый редактор
Дата добавления03/08/08
Последнее изменение16/06/11



Содержание

Creating a new entity

In this document we will go through creating a new entity (in Lua), with support for flowgraphs. I’ll be using MyMod and MyEntity to identify our mod and the new entity. You can of course change this to whatever you like.

Lua compiler

It is handy to have a lua compiler to check for syntax errors in your script files. Also, the 'Reload Script' button in the Sandbox Editor uses this. Lua entity tutorial reloadscript.jpg

You can get the lua compiler here.

Extract LuaCompiler.exe to <crysis folder>/bin32/ or <crysis folder>/bin64/


Setting up an entity

Now, let's get started. First of all we’re going to create a mod folder. Browse to your Crysis folder and create a Mods folder. In that folder create a MyMod folder. Next, let’s create a shortcut in that folder so we can launch the editor with our mod loaded. We have to add –MOD MyMod to the shortcut so the editor knows what mod to load. It could be something like this:

"C:\Program Files\Electronic Arts\Crytek\Crysis\Bin32\Editor.exe" -MOD MyMod


After that, we need to let the engine know we have a new entity which needs to be loaded. We will need to create a new folder in our mod-directory: Game, and in there another new dir: entities. In that folder create a new file called MyEntity.ent.

Now let’s edit our created MyEntity.ent in a text-editor of your choice. This is the content that needs to be in the file:

<Entity Name="MyEntity" Script="Scripts/Entities/MyMod/MyEntity.lua" />


Let’s look at that a bit closer. It’s just a simple xml file with the name in it, and the location to a .lua file. We will create that scriptfile in the next chapter. Let’s get to the scripting part.


The entity script (basics)

Now that we have the entity setup, we need to create the lua file for it. The lua file will contain the code and the properties of our entity. If you look at our MyEntity.ent file you can see the path Scripts/Entities/MyMod/MyEntity.lua. We need to create this file, as it doesn’t exist yet.


Предупреждение:
Because Sandbox doesn’t like editing and reloading entities from our mod-folder, we will develop our entity inside the default Crysis/Game/ folder. Once our entity is ready, we can move it to our Mod folder.


Let’s create a new file in Crysis/Game/Scripts/Entities/MyMod/ (create directories if needed). The file name should be MyEntity.lua, which is specified in our MyEntity.ent file. Now that we have our new file, open it in a text editor. I will now go step by step and analyze the code we have to type. If you want, you can copy and paste the whole file at the end of this tutorial.

Lua

MyEntity = {
	Properties = {
		fMessageDuration = 10.0,
		sMessage = "Use me",
		bDoesNothing = 0,
		object_MyModel = "Editor/Objects/objective.cgf",
		Colors = {
			clrColor = {x=1, y=0, z=0 },
		},
	},
	Editor = { Icon= "Target.bmp", },
};


Let’s analyze that code a bit. First we define our entity with

MyEntity = { … }


Next we set up our Properties. Everything in here will be properties you can adjust in the Sandbox editor, like in the picture on the right, which are the properties of the Tornado entity. Our properties are:

fMessageDuration The time a message will be shown when we use our entity.
sMessage The actual message to show.
bDoesNothing This actually does nothing
object_MyModel The model that our entity has
clrColor The color of our message


A few things to note: our variables have prefixes like f,s,b,clr,object_ . They are there for a reason. It lets Sandbox know what kind of variable it is. That way we can’t put in text in our MessageDuration, we get a Open file dialog for our models, etc… . Here’s a small list with the most common prefixes:

b boolean
s string
f float
object_ model(with file open dialog)
clr Color (with colorpick dialog)


Also note that our clrColor is in another table in the Properties table. This way we will have a subcategory in Sandbox. After our Properties come the Editor properties. Here we set the Icon which sandbox will use.

Editor = { Icon= "Target.bmp", },


After that you can also set up some private variables which you can use in the scripts. But for this tutorial we won’t need those.


We can already take a peek in the editor, and I suggest you do so. That way we can check if our files are in the right place, and if the editor loads our file.

So browse to our mod folder we created and launch the editor via the shortcut to sandbox. No need to load a map, in the rollup bar select Entity and there we should find our entity listed, nicely found in a map called MyMod.

If that isn’t the case, something is wrong. Make sure you did everything right, launched the editor with our shortcut, made the .ent file, made the .lua file.

If you double click our entity you should see our properties we defined in the lua file:

Lua entity tutorial properties.jpg

Here you can also see what the prefixes have done. Also note that everything is sorted alphabetically, without prefixes.

Now, our entity has some properties, but it doesn’t do anything. You can even try to set a model, it won’t show up. So let’s code that in first. (No need to close the Editor now, we can keep it open for quick debugging) Open the MyEntity.lua script again and add

Lua

function MyEntity:OnInit()
	self:OnReset();
end


The OnInit function gets called when our entity gets initialized, typically when loading the map. We call a function called OnReset(), which we will now make.

Lua

function MyEntity:OnReset()
	if (self.Properties.object_MyModel ~= "") then
		self:LoadObject(0, self.Properties.object_MyModel);
	end
end


There’s a bit more to explain now. The function gets called on its own when we jump into the editor. (We also call it from OnInit here, which is pretty common)

This first checks if the model is not an empty string. After that it loads the model. Save your file now. Let’s get back to the editor. Open a map if you didn’t do so yet. Place the MyEntity entity on it. You should see a model now. If not, reload the entity script via the Reload Script button.

Now try changing the model again. It doesn’t seem to work. We need to add some more code for that.

Lua

function MyEntity:OnPropertyChange()
	self:OnReset();
end


OnPropertyChange() gets called whenever we change a property in the editor. Because of that, we need to reload the model. So we call OnReset again which will load the new model.

Let’s take a look at what we have by now. An entity which has a model, we can change that model, but that’s about it. It doesn’t do anything. Let’s make it so we can use our entity.

Lua

function MyEntity:IsUsable(user)
	return 2;
end
 
function MyEntity:GetUsableMessage(idx)
	return self.Properties.sMessage;
end


The first function IsUsable() returns 2. This makes it so that we can use our entity. The second function defines what message we will get when we want to use the entity. We return the message we set in our properties. Now our entity will be useable, but it still won’t do anything. For that, we need, you guessed it, more code.

Lua

function MyEntity:OnUsed(user, idx)
	CryAction.Persistant2DText(
		string.format("Used> %s", self.Properties.sMessage),
		2,
		self.Properties.Colors.clrColor,
		"OnUsed",
		self.Properties.fMessageDuration
	);
end


What this does is print a message to our screen. The message is “Used: OurMessage”, OurMessage gets replaced with the message specified in the properties.

Second argument is the size of our message, currently set to 2.

Next argument is the color, we also get it from the properties.

4th argument is the name of the message 5th and last argument is the duration of our message, in seconds.

We can try that out now, jump back to the editor, click the reload script button and jump ingame. Run up to our entity and press ‘f’. You should see the message specified in the properties. Try changing some of the values in the properties panel and jump ingame to see the results.


This is what you should have by now:

Lua

MyEntity = {
	Properties = {
		fMessageDuration = 10.0,
		sMessage = “Use me”,
		bDoesNothing = 0,
		object_MyModel = "Editor/Objects/objective.cgf",
		Colors = {
			clrColor = {x=1, y=0, z=0 },
		},
	},
	Editor = { Icon= "Target.bmp", },
};
 
function MyEntity:OnInit()
	self:OnReset();
end
 
function MyEntity:OnReset()
	if (self.Properties.object_MyModel ~= "") then
		self:LoadObject(0, self.Properties.object_MyModel);
	end
end
 
function MyEntity:OnPropertyChange()
	self:OnReset();
end
 
function MyEntity:IsUsable(user)
	return 2;
end
 
function MyEntity:GetUsableMessage(idx)
	return self.Properties.sMessage;
end
 
function MyEntity:OnUsed(user, idx)
	CryAction.Persistant2DText(
		string.format("Used> %s", self.Properties.sMessage),
		2,
		self.Properties.Colors.clrColor,
		"OnUsed",
		self.Properties.fMessageDuration
	);
end


Extending our entity

We have a basic entity now, let’s add some more interesting/advanced stuff. We will base this on the entity we just made. First, let’s make our model physicalized. We will make a new function for this called PhysicalizeThis, and call it in the OnReset function.

Lua

function MyEntity:OnReset()
	if (self.Properties.object_MyModel ~= "") then
		self:LoadObject(0, self.Properties.object_MyModel);
	end
	self:PhysicalizeThis(); -- This is the new line
end
 
function MyEntity:PhysicalizeThis()
	local Physics = {
		bRigidBody=1,
		bRigidBodyActive = 0,
		bRigidBodyAfterDeath =1,
		bResting = 1,
		Density = -1,
		Mass = 5,
	};
	EntityCommon.PhysicalizeRigid( self, 0, Physics, 1 );
end


In the PhysicalizeThis function we define Physics which contains some vars you should recognize from the editor. Then we pass them along EntityCommon.PhysicalizeRigid(). And now our model is physicalized, it’s that simple.

We can jump in the editor and knock over our entity now. One of the last things we will do now is clone our entity when we press it. Lets’ get coding. We need to add code when we use our entity, so let’ go there and call a new function.

Lua

function MyEntity:OnUsed(user, idx)
	CryAction.Persistant2DText(
		string.format("Used> %s", self.Properties.sMessage),
		2,
		self.Properties.Colors.clrColor,
		"OnUsed",
		self.Properties.fMessageDuration
	);
	self:Clone(); -- This is the new line
end
 
function MyEntity:Clone()
	local clone = { class = "MyEntity", };
	clone.properties = self.Properties;
 
	local ent = System.SpawnEntity(clone);
	local pos = self:GetPos();
 
	pos.x = pos.x + 1;
	ent:SetPos(pos);
end


This clones our entity. First we set the class to MyEntity, which will spawn our entity with the default values. We want the settings of our main entity though, so we copy the properties of our self. Then we spawn the entity, get the position of our main entity and increase the x value by 1. This will make it so we can actually see if it’s cloned or not. After that, we set the position again to our new position.

And done, we got our cloning function. You can test it out in the editor.


Adding flowgraph support

For this particular entity it’s a bit stupid, but it’s the idea that counts. All the flowgraph info goes into FlowEvents. We have to define that, so let’s do that now.

Lua

MyEntity.FlowEvents = {
	Inputs = {
		Clone = { MyEntity.Event_Clone, "bool" },
		Message = { MyEntity.Event_Message, "string" },
	},
	Outputs = { Used = "bool", },
}


When we look at it for a second, it doesn’t seem that hard. We have the inputs and the outputs part. In the Inputs part we got a Clone and a Message. In the output we have a Used which will get called when we used our entity.

But what’s the stuff after Clone = and Message = ? MyEntity.Event_Clone and MyEntity.Event_Message, where does that come from? You’re right, it comes out of nowhere, so we need to declare some functions.

Lua

function MyEntity:Event_Clone(sender)
	self:Clone();
end
 
function MyEntity:Event_Message(sender, msg)
	self.Properties.sMessage = msg;
end


Event_Clone just calls our Clone function we made earlier. With Event_Message we can change our property sMessage from the flowgraph. That covers our Inputs, but how are the outputs handled? We need to add some code that triggers that event, and it’s actually quite simple:

Lua

function MyEntity:OnUsed(user, idx)
	CryAction.Persistant2DText(
		string.format("Used> %s", self.Properties.sMessage),
		2,
		self.Properties.Colors.clrColor,
		"OnUsed",
		self.Properties.fMessageDuration
	);
	self:Clone(); 
	self:ActivateOutput( "Used" ,true ); -- This is the new line
end


We just set that Used value we defined earlier in the output Flowevents and set it to true.

That was it, our flowgraph should work now. Let’s give it a little test. Go to the editor and reload your entity, then click on the Create button under Flowgraph. Give it some name and then we got a blank flowgraph. Now rightclick and select ‘Add Graph Default Entity’. We have the entity on our flowgraph now.

Let’s give it a little test:

Lua entity tutorial flowgraph sample.jpg

And now once we use our entity, we will see a “Flowgraph is here” message. Note that it this will only show on the original entity. So that sums it up, we have a custom entity which doesn’t do anything spectacular, with flowgraph support.


Full code

Lua

MyEntity = {
	Properties = {
		fMessageDuration = 10.0,
		sMessage = "Use me",
		bDoesNothing = 0,
		object_MyModel = "Editor/Objects/objective.cgf",
		Colors = {
			clrColor = {x=1, y=0, z=0 },
		},
	},
	Editor = { Icon= "Target.bmp", },
};
 
function MyEntity:OnInit()
	self:OnReset();
end
 
function MyEntity:OnReset()
	if (self.Properties.object_MyModel ~= "") then
		self:LoadObject(0, self.Properties.object_MyModel);
	end
	self:PhysicalizeThis();
end
 
function MyEntity:OnPropertyChange()
	self:OnReset();
end
 
function MyEntity:IsUsable(user)
	return 2;
end
 
function MyEntity:GetUsableMessage(idx)
	return self.Properties.sMessage;
end
 
function MyEntity:OnUsed(user, idx)
	CryAction.Persistant2DText(
		string.format("Used> %s", self.Properties.sMessage),
		2,
		self.Properties.Colors.clrColor,
		"OnUsed",
		self.Properties.fMessageDuration
	);
	self:Clone();
end
 
function MyEntity:PhysicalizeThis()
	local Physics = {
		bRigidBody=1,
		bRigidBodyActive = 0,
		bRigidBodyAfterDeath =1,
		bResting = 1,
		Density = -1,
		Mass = 5,
	};
	EntityCommon.PhysicalizeRigid( self, 0, Physics, 1 );
end
 
function MyEntity:Clone()
	local clone = { class = "MyEntity", };
	clone.properties = self.Properties;
 
	local ent = System.SpawnEntity(clone);
	local pos = self:GetPos();
 
	pos.x = pos.x + 1;
	ent:SetPos(pos);
end
 
function MyEntity:Event_Clone(sender)
	self:Clone();
end
 
function MyEntity:Event_Message(sender, msg)
	self.Properties.sMessage = msg;
end
 
MyEntity.FlowEvents = {
	Inputs = {
		Clone = { MyEntity.Event_Clone, "bool" },
		Message = { MyEntity.Event_Message, "string" },
	},
	Outputs = { Used = "bool", },
}