Modding:Advanced Blocks: Difference between revisions
m (Updated navbox to new code navbox.) |
|||
(17 intermediate revisions by 8 users not shown) | |||
Line 1: | Line 1: | ||
<languages /> | |||
__FORCETOC__ | __FORCETOC__ | ||
<translate> | |||
<!--T:1--> | |||
{{GameVersion|1.19.3}} | |||
</translate> | |||
<translate> | |||
<!--T:2--> | |||
This ''code mod'' tutorial requires a development environment. If you do not have one, please read and complete the [[Setting up your Development Environment]] page. It is also highly recommended to have read and completed the [[Basic Block]] tutorial. | |||
</translate> | |||
= <translate><!--T:3--> Creating a Trampoline</translate> = | |||
<translate> | |||
<!--T:4--> | |||
In this tutorial will we create a block with more advanced functionality: a trampoline. | |||
</translate> | |||
= | == <translate><!--T:5--> Block Assets</translate> == | ||
<translate> | |||
<!--T:6--> | |||
Similar to our [[Basic Block]], the first things we need are the assets of the block. Create a <code>trampoline.json</code> blocktype based on our block created in the other tutorial. | |||
<!--T:7--> | |||
In our blocktype file, we need to add the <code>class</code> property. This property essentially tells our new block to be controlled by a certain C# class. | |||
</translate> | |||
<syntaxhighlight lang="json"> | <syntaxhighlight lang="json"> | ||
class: "trampoline", | class: "trampoline", | ||
</syntaxhighlight> | </syntaxhighlight> | ||
We will create this class | <translate> | ||
You can download the assets of the mod [https://wiki.vintagestory.at/images/b/b9/Trampoline_-_No_CS_FILE.zip here]. | <!--T:8--> | ||
All you need to do is to place this zip file in your <code>assets</code> directory in your development project. | We will create this class in our development environment to give the block the desired functionality. You can download the assets of the mod [https://wiki.vintagestory.at/images/b/b9/Trampoline_-_No_CS_FILE.zip here]. | ||
All you need to do is to place the contents of this zip file in your <code>assets</code> directory in your development project. | |||
</translate> | |||
== The Block Class == | == <translate><!--T:9--> The Block Class</translate> == | ||
<translate> | |||
<!--T:10--> | |||
To create our mod, we need a number of new <code>*.cs</code> files in our project. | |||
</translate> | |||
=== The Mod System === | === <translate><!--T:11--> The Mod System</translate> === | ||
In order to create a mod | <translate> | ||
<!--T:12--> | |||
In order to create a mod, we needs to create a class that extends <code>ModSystem</code>. This will allow use to register all kinds of stuff, but for now we will stick to only registering our block class. | |||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 35: | Line 56: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Now you need to override the <code>Start(ICoreAPI)</code> method and register the class. | <translate> | ||
<!--T:13--> | |||
Now you need to override the <code>Start(ICoreAPI)</code> method and register the class. | |||
<!--T:37--> | |||
The <code>RegisterBlockClass</code> function has two parameters: the first is our block class ID, noteably how we use this class in our blocktype json files. Ensure that this is identical to the class we specified in our earlier asset file. The second parameter is the type of our block class. | |||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 48: | Line 75: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<translate> | |||
<!--T:14--> | |||
This should be marked as a syntax error because there is no <code>TrampolineBlock</code> class yet. | This should be marked as a syntax error because there is no <code>TrampolineBlock</code> class yet. | ||
</translate> | |||
=== The Block Class === | === <translate><!--T:15--> The Block Class</translate> === | ||
<translate> | |||
<!--T:16--> | |||
Let's create our block class. When naming block scripts, it is recommended to name them in the format "{Name}Block". In the case of the trampoline, we shall name our script <code>TrampolineBlock.cs</code>. Any block class has to extend <code>Block</code>, giving it the functionality we need to access: | |||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
public class TrampolineBlock : Block | public class TrampolineBlock : Block | ||
Line 61: | Line 93: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<translate> | |||
<!--T:17--> | |||
This should solve all syntax errors. | This should solve all syntax errors. | ||
<!--T:18--> | |||
So how do we implement a bouncy block? It's pretty helpful to take a look at the [https://apidocs.vintagestory.at/api/Vintagestory.API.Common.Block.html api documentation] to find a proper way to implement it. | So how do we implement a bouncy block? It's pretty helpful to take a look at the [https://apidocs.vintagestory.at/api/Vintagestory.API.Common.Block.html api documentation] to find a proper way to implement it. | ||
The method <code>void | <!--T:19--> | ||
The method <code>void OnEntityCollide(IWorldAccessor world, Entity entity, BlockPos pos, BlockFacing facing, Vec3d collideSpeed, bool isImpact)</code> seems to be a good way to implement a bouncy functionality. Note that every trampoline block placed in the game will [[Modding:Block_Cardinality|share]] the same instance of <code>TrampolineBlock</code>. Because that object is shared by multiple blocks, it does not have a field for the block position. That's why the event handler includes the <code>pos</code> parameter. | |||
<!--T:20--> | |||
'''When should an entity bounce?''' | '''When should an entity bounce?''' | ||
# The entity should bounce in the moment it lands on top of the block and not if it is standing on it already. Therefore <code>isImpact</code> has to be <code>true</code> | # The entity should bounce in the moment it lands on top of the block and not if it is standing on it already. Therefore, <code>isImpact</code> has to be <code>true</code>. | ||
# | # The entity should be colliding vertically. The sides of the block shouldn't push an entity away. So the <code>axis</code> of the <code>facing</code> has to be <code>Y</code>. | ||
<!--T:21--> | |||
'''How can we make the entity bounce?''' | '''How can we make the entity bounce?''' | ||
<!--T:38--> | |||
In order to make an entity bounce, we need to change its direction. Therefore we can simply revert its motion. The faster the entity will be when during the collision the further it will be pushed away. But simply reverting the motion wouldn't be ideal. The entity would never lose its motion and bounce endless. So let's go for something smaller and make the entity lose 20% of its motion each bounce: | In order to make an entity bounce, we need to change its direction. Therefore we can simply revert its motion. The faster the entity will be when during the collision the further it will be pushed away. But simply reverting the motion wouldn't be ideal. The entity would never lose its motion and bounce endless. So let's go for something smaller and make the entity lose 20% of its motion each bounce: | ||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
entity.Pos.Motion.Y *= -0.8; | entity.Pos.Motion.Y *= -0.8; | ||
</syntaxhighlight>The <code>*=</code> is a shorthand way of writing:<syntaxhighlight lang="c#"> | |||
entity.Pos.Motion.Y = entity.Pos.Motion.Y * -0.8; | |||
</syntaxhighlight> | </syntaxhighlight> | ||
---- | ---- | ||
<translate> | |||
<!--T:22--> | |||
If we put everything together it should look like this: | If we put everything together it should look like this: | ||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
public class TrampolineBlock : Block | public class TrampolineBlock : Block | ||
{ | { | ||
public override void | public override void OnEntityCollide(IWorldAccessor world, Entity entity, BlockPos pos, BlockFacing facing, Vec3d collideSpeed, bool isImpact) | ||
{ | { | ||
if (isImpact && facing.Axis == EnumAxis.Y) | if (isImpact && facing.Axis == EnumAxis.Y) | ||
Line 94: | Line 140: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<translate> | |||
<!--T:23--> | |||
Although this code works already, some sound effects would be rather nice. We can implement it by adding a sound link field to our block, which can use to play the <code>game:tick</code> sound. | Although this code works already, some sound effects would be rather nice. We can implement it by adding a sound link field to our block, which can use to play the <code>game:tick</code> sound. | ||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 100: | Line 149: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<translate> | |||
<!--T:24--> | |||
This <code>tickSound</code> will played every time an entity bounces: | This <code>tickSound</code> will played every time an entity bounces: | ||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
Line 108: | Line 160: | ||
---- | ---- | ||
<translate> | |||
<!--T:25--> | |||
If you have done everything right, your file should look similar to this: | If you have done everything right, your file should look similar to this: | ||
</translate> | |||
<syntaxhighlight lang="c#"> | <syntaxhighlight lang="c#"> | ||
using Vintagestory.API.Common; | using Vintagestory.API.Common; | ||
Line 130: | Line 185: | ||
public AssetLocation tickSound = new AssetLocation("game", "tick"); | public AssetLocation tickSound = new AssetLocation("game", "tick"); | ||
public override void OnEntityCollide(IWorldAccessor world, Entity entity, BlockPos pos, BlockFacing facing, bool isImpact) | public override void OnEntityCollide(IWorldAccessor world, Entity entity, BlockPos pos, BlockFacing facing, Vec3d collideSpeed, bool isImpact) | ||
{ | { | ||
if (isImpact && facing.Axis == EnumAxis.Y) | if (isImpact && facing.Axis == EnumAxis.Y) | ||
Line 142: | Line 197: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<translate> | |||
<!--T:26--> | |||
Of course you can download the file directly [https://wiki.vintagestory.at/images/8/8a/Trampoline.cs Trampoline.cs]. | Of course you can download the file directly [https://wiki.vintagestory.at/images/8/8a/Trampoline.cs Trampoline.cs]. | ||
</translate> | |||
== Testing == | == <translate><!--T:27--> Testing</translate> == | ||
<translate> | |||
<!--T:28--> | |||
Finally we can run our first test. Looks pretty good, right? | Finally we can run our first test. Looks pretty good, right? | ||
</translate> | |||
<youtube>Kg8J_rNOweU</youtube> | <youtube>Kg8J_rNOweU</youtube> | ||
<translate> | |||
<!--T:29--> | |||
'''Hint''': Use the client command <code>.tfedit</code> if you want to adjust the block position, rotation and scale in Hands, in GUI, when dropped on the ground or in third person mode. | '''Hint''': Use the client command <code>.tfedit</code> if you want to adjust the block position, rotation and scale in Hands, in GUI, when dropped on the ground or in third person mode. | ||
</translate> | |||
== Distribution == | == <translate><!--T:30--> Distribution</translate> == | ||
<translate>=== Using the new Mod Template === <!--T:31--> | |||
If using the mod template setup, follow the instructions on [[Modding:Setting up your Development Environment#Packaging the Mod|Setting up your Development Environment]] to pack and distribute your mod. | |||
=== Using the (old) Modtools === <!--T:39--> | |||
If using the modtools program, open the modtools and type in <code>pack <your mod id></code>. Now you can take the zip file and share it with other people. It will work in the same way as ordinary mods, you can install it by copying it into the <code>mods</code> folder. | |||
</translate> | |||
= <translate><!--T:32--> Mod Download</translate> = | |||
<translate> | |||
<!--T:33--> | |||
Here is my version: | Here is my version: | ||
* for VS v1.9: [https://wiki.vintagestory.at/images/ | * for VS v1.9: [https://wiki.vintagestory.at/images/2/24/Trampoline_vs1.9_v1.0.0.zip Trampoline_vs1.9_v1.0.0.zip] | ||
* for VS v1.5: [https://wiki.vintagestory.at/images/c/ce/Trampoline.zip Trampoline.zip] | * for VS v1.5: [https://wiki.vintagestory.at/images/c/ce/Trampoline.zip Trampoline.zip] | ||
</translate> | |||
= <translate><!--T:34--> Moving Forward</translate> = | |||
<translate> | |||
<!--T:35--> | |||
Now that you've successfully made an advanced block you can go even further by learning how to utilize'''[[Modding:Block Entity| Block Entities]]''' and how to create your own '''[[Modding:Adding Block Behavior | Block Behaviors]]'''. Both of these tutorials will teach you how to add even more mechanics to your custom blocks. | |||
<!--T:36--> | |||
Or, you can try out making an '''[[Modding:Advanced Items | Advanced Item]]''' if you haven't already. | |||
</translate> | |||
{{Navbox/ | {{Navbox/codemodding}} |
Latest revision as of 16:59, 27 March 2024
This page was last verified for Vintage Story version 1.19.3.
This code mod tutorial requires a development environment. If you do not have one, please read and complete the Setting up your Development Environment page. It is also highly recommended to have read and completed the Basic Block tutorial.
Creating a Trampoline
In this tutorial will we create a block with more advanced functionality: a trampoline.
Block Assets
Similar to our Basic Block, the first things we need are the assets of the block. Create a trampoline.json
blocktype based on our block created in the other tutorial.
In our blocktype file, we need to add the class
property. This property essentially tells our new block to be controlled by a certain C# class.
class: "trampoline",
We will create this class in our development environment to give the block the desired functionality. You can download the assets of the mod here.
All you need to do is to place the contents of this zip file in your assets
directory in your development project.
The Block Class
To create our mod, we need a number of new *.cs
files in our project.
The Mod System
In order to create a mod, we needs to create a class that extends ModSystem
. This will allow use to register all kinds of stuff, but for now we will stick to only registering our block class.
public class TrampolineMod : ModSystem
{
}
Now you need to override the Start(ICoreAPI)
method and register the class.
The RegisterBlockClass
function has two parameters: the first is our block class ID, noteably how we use this class in our blocktype json files. Ensure that this is identical to the class we specified in our earlier asset file. The second parameter is the type of our block class.
public class TrampolineMod : ModSystem
{
public override void Start(ICoreAPI api)
{
base.Start(api);
api.RegisterBlockClass("trampoline", typeof(TrampolineBlock));
}
}
This should be marked as a syntax error because there is no TrampolineBlock
class yet.
The Block Class
Let's create our block class. When naming block scripts, it is recommended to name them in the format "{Name}Block". In the case of the trampoline, we shall name our script TrampolineBlock.cs
. Any block class has to extend Block
, giving it the functionality we need to access:
public class TrampolineBlock : Block
{
}
This should solve all syntax errors.
So how do we implement a bouncy block? It's pretty helpful to take a look at the api documentation to find a proper way to implement it.
The method void OnEntityCollide(IWorldAccessor world, Entity entity, BlockPos pos, BlockFacing facing, Vec3d collideSpeed, bool isImpact)
seems to be a good way to implement a bouncy functionality. Note that every trampoline block placed in the game will share the same instance of TrampolineBlock
. Because that object is shared by multiple blocks, it does not have a field for the block position. That's why the event handler includes the pos
parameter.
When should an entity bounce?
- The entity should bounce in the moment it lands on top of the block and not if it is standing on it already. Therefore,
isImpact
has to betrue
. - The entity should be colliding vertically. The sides of the block shouldn't push an entity away. So the
axis
of thefacing
has to beY
.
How can we make the entity bounce?
In order to make an entity bounce, we need to change its direction. Therefore we can simply revert its motion. The faster the entity will be when during the collision the further it will be pushed away. But simply reverting the motion wouldn't be ideal. The entity would never lose its motion and bounce endless. So let's go for something smaller and make the entity lose 20% of its motion each bounce:
entity.Pos.Motion.Y *= -0.8;
The *=
is a shorthand way of writing:
entity.Pos.Motion.Y = entity.Pos.Motion.Y * -0.8;
If we put everything together it should look like this:
public class TrampolineBlock : Block
{
public override void OnEntityCollide(IWorldAccessor world, Entity entity, BlockPos pos, BlockFacing facing, Vec3d collideSpeed, bool isImpact)
{
if (isImpact && facing.Axis == EnumAxis.Y)
{
entity.Pos.Motion.Y *= -0.8;
}
}
}
Although this code works already, some sound effects would be rather nice. We can implement it by adding a sound link field to our block, which can use to play the game:tick
sound.
public AssetLocation tickSound = new AssetLocation("game", "tick");
This tickSound
will played every time an entity bounces:
world.PlaySoundAt(tickSound, entity.Pos.X, entity.Pos.Y, entity.Pos.Z);
If you have done everything right, your file should look similar to this:
using Vintagestory.API.Common;
using Vintagestory.API.Common.Entities;
using Vintagestory.API.MathTools;
namespace VSExampleMods
{
public class TrampolineMod : ModSystem
{
public override void Start(ICoreAPI api)
{
base.Start(api);
api.RegisterBlockClass("trampoline", typeof(TrampolineBlock));
}
}
public class TrampolineBlock : Block
{
public AssetLocation tickSound = new AssetLocation("game", "tick");
public override void OnEntityCollide(IWorldAccessor world, Entity entity, BlockPos pos, BlockFacing facing, Vec3d collideSpeed, bool isImpact)
{
if (isImpact && facing.Axis == EnumAxis.Y)
{
world.PlaySoundAt(tickSound, entity.Pos.X, entity.Pos.Y, entity.Pos.Z);
entity.Pos.Motion.Y *= -0.8;
}
}
}
}
Of course you can download the file directly Trampoline.cs.
Testing
Finally we can run our first test. Looks pretty good, right?
Hint: Use the client command .tfedit
if you want to adjust the block position, rotation and scale in Hands, in GUI, when dropped on the ground or in third person mode.
Distribution
Using the new Mod Template
If using the mod template setup, follow the instructions on Setting up your Development Environment to pack and distribute your mod.
Using the (old) Modtools
If using the modtools program, open the modtools and type in pack <your mod id>
. Now you can take the zip file and share it with other people. It will work in the same way as ordinary mods, you can install it by copying it into the mods
folder.
Mod Download
Here is my version:
- for VS v1.9: Trampoline_vs1.9_v1.0.0.zip
- for VS v1.5: Trampoline.zip
Moving Forward
Now that you've successfully made an advanced block you can go even further by learning how to utilize Block Entities and how to create your own Block Behaviors. Both of these tutorials will teach you how to add even more mechanics to your custom blocks.
Or, you can try out making an Advanced Item if you haven't already.
Code Modding | |||||||
---|---|---|---|---|---|---|---|
Basics | Code Mods • Preparing For Code Mods • Creating A Code Mod | ||||||
Tutorials |
|
||||||
Advanced | Server-Client Considerations • Setting up your Development Environment • Advanced Blocks • Advanced Items • Block and Item Interactions • Block Behavior • Block Entity • Particle Effects • World Access • Inventory Handling • Commands • GUIs • Network API • Monkey patching (Harmony) | ||||||
Data Management | Savegame Data Storage • ModConfig File • Chunk Data Storage • TreeAttribute | ||||||
Worldgen | WorldGen API • NatFloat • EvolvingNatFloat | ||||||
Rendering | Shaders and Renderers |
Wondering where some links have gone?
The modding navbox is going through some changes! Check out Navigation Box Updates for more info and help finding specific pages.
Modding | |
---|---|
Modding Introduction | Getting Started • Theme Pack |
Content Modding | Content Mods • Developing a Content Mod • Basic Tutorials • Intermediate Tutorials • Advanced Tutorials • Content Mod Concepts |
Code Modding | Code Mods • Setting up your Development Environment |
Property Overview | Item • Entity • Block • Block Behaviors • Block Classes • Block Entities • Block Entity Behaviors • World properties |
Workflows & Infrastructure | Modding Efficiency Tips • Mod-engine compatibility • Mod Extensibility • VS Engine |
Additional Resources | Community Resources • Modding API Updates • Programming Languages • List of server commands • List of client commands • Client startup parameters • Server startup parameters Example Mods • API Docs • GitHub Repository |