Keen:Mechanical System: Difference between revisions

From Medieval Engineers Wiki
Jump to navigation Jump to search
(Created page with "{{Header|Coming Soon}} Category:Keen_Modding_Guides")
 
mNo edit summary
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
{{Header|Coming Soon}}
{{SEO|image_url=http://www.medievalengineerswiki.com/images/b/b7/KeenLogoBig.png|description=The mechanical system is a simple system that provides power to machinery, without having to rely on physics, and it also has very low performance impact.}}
{{Keen:OCH}}
 
 
The mechanical system is a simple system that provides power to machinery, without having to rely on physics, and it also has very low performance impact.
 
{{Version <!-- Do not change the version until the entire page is up-to-date -->
|release=0|major=6|minor=1|suppress=true}}
==MyMechanicalPowerSystem==
The brain of the mechanical system, that is added to all grids. It holds all of the mechanical groups for the grid that it is attached to and ensures that when you register a new component (node) in the system, it gets added to the proper group or a new one is created for it.
 
===Definition===
<syntaxhighlight lang="xml">
<Definition xsi:type="MyObjectBuilder_MechanicalPowerSystemDefinition">
  <Id Type="MechanicalPowerSystem" Subtype="CubeGridLarge" />
  <TransmissionFrictionMultiplier>1.08</TransmissionFrictionMultiplier>
</Definition>
</syntaxhighlight>
 
'''Id:''' mandatory field, type for mechanical system is MechanicalPowerSystem.
 
'''TransmissionFrictionMultiplier:''' the amount of multiplicative friction added per transmission block in a group.
 
 
{{TextBox|NOTICE: This definition is only useful when you want to override its properties, since this component is really only useful on a grid, and all of them have this definition added by default.}}
 
==Groups==
A group represents a collection of interconnected mechanical components. It acts as a singular unit, meaning that anything that is computed for the group, is going to be applicable for any and all of the components it stands for.
 
===Power Calculation===
For a group to be considered functional, it needs to have more power than friction. This means for a group to be useful at all, it needs to contain at least one power source, for example, a windmill. Each time a new block is added to the group or a block is removed from the group, some of its properties need to be recalculated.
 
 
'''Power Formula:'''
 
<code>RP = (P - F * M^N) / P;</code>
 
'''P:''' Total power of all connected sources.
 
'''F:''' Total friction of all connected sources and sink.
 
'''M:''' Transmission friction multiplier.
 
'''N:''' Number of connected transmissions.
 
'''RP:''' Relative power.
 
If the calculation would result in a negative value, it is instead reset to 0, as the relative power only makes sense between 0 and 1. A group is considered to be powered when RP > 0. All numbers, with the exception of relative power, are integers.
 
===Rotational Alignment===
Mechanical power in the game is represented in the form of rotation. Any mechanical part, except for the power source, can spin in either direction. The possible directions are clockwise (also referred to as left), and counter-clockwise (also referred to as right). A mechanical source comes with a pre-determined rotation, which is used to calculate rotation on all other connected blocks. If there are more sources connected to one group of blocks, the calculation is done from each one of them, breadth-first. If the calculation encounters a problem, such as two blocks that are attached, but the rotations passed on from their sources do not match, the whole mechanical group will come to a halt until the issue is resolved. This can only be resolved by removing one of the touching blocks or the source that is providing the "wrong" rotation.
 
==MyMechanicalComponentBase==
Mechanical components are nodes in the group. MyMechanicalComponentBase is a base implementation that includes all of the common functionality, such as registering in the system, detecting connections, adding or removing connections, changing animations and providing events. It also provides some common definition fields.
 
===Definition===
The base component includes a couple of definition fields, that are shared across all mechanical blocks.
 
<syntaxhighlight lang="xml" line>
<Definition xsi:type="MyObjectBuilder_MechanicalTransmissionComponentDefinition">
  <Id Type="MechanicalTransmissionComponent" Subtype="TransmissionWood" />
  <Connection>connection_forward</Connection>
  <Connection>connection_backward</Connection>
  <ClockwiseAnimation>cw</ClockwiseAnimation>
  <CounterClockwiseAnimation>ccw</CounterClockwiseAnimation>
</Definition>
</syntaxhighlight>
 
'''Connection:''' name of the model dummy, whose properties are supposed to be used for mechanical connections.
 
'''ClockwiseAnimation:''' name of the animation to be played when this block is considered to be rotating clockwise. The animation has to be defined in a ''SubpartAnimationComponent''.
 
'''CounterClockwiseAnimation:''' name of the animation to be played when this block is considered to be rotating counter-clockwise. The animation has to be defined in a ''SubpartAnimationComponent''. In most cases, the counter-clockwise animation is just a reversed clockwise animation.
 
===Connections===
As you may have noticed in the definition, we are using model dummies to specify the location of what we refer to as connection ports. There are two important factors to take into consideration when adding a port dummy to your model:
*Position
*Local Rotation
 
''Position'' is fairly straightforward: if you want two blocks to be connectable via a mechanical system, their port dummies need to intersect. The code, that does this check, completely disregards the size of the dummy and only requires the centers of the two dummies to be as close to each other as possible. There is very little margin for error when checking the distance, in order to rule out false positives, so make sure to place your dummies as precisely as possible. As was already mentioned, the size of the dummy does not play a role, so set it to something that makes it comfortable to work with in your editor of choice.
 
''Local Rotation'' is a little more tricky - it is not actually the whole rotation matrix that the game is interested in. We only care about the local x-axis of the dummy. The connection check looks at the local axes of the two intersecting dummies, and if their x-axes are running in parallel, a connection can be made. But now comes the actual tricky part - if both axes are running in the same direction, the connection is considered to be a direct connection. If the two axes are running opposite to each other, the connection is inverted. In case the axes are not parallel at all, a connection cannot be made.
 
While both connection types (direct and inverted) are valid and will transfer power, it is important to watch the rotation when adding dummies to the block. The orientation of the port dummy is never absolute, it is always considered in relation to the other ports on the block. Is it very important to have it done correctly so that you can set your animations accordingly. It helps a lot if you think of the ports as input and output, and then try to visualize how you want each part to rotate, using pen and paper can help a lot in this step. You will find a simple rotation table below, which shows how the rotation works for a block with two ports. It is pretty much the same for any amount of ports, you just need to do a lot more drawing.
 
{| class="wikitable"
|-
! Input port orientation !! Input port rotation !! Output port orientation !! Output port rotation
|-
| in || left || out || left
|-
| in || left || in || right
|-
| in || right || out || right
|-
| in || right || in || left
|-
| Out || left || in || left
|-
| Out || left || out || right
|-
| Out || right || in || right
|-
| Out  || right || out || left
|}
 
'''in:''' the local x-axis is pointing into the block space.
 
'''out:''' the local x-axis is pointing out of the block space.
 
'''left:''' clockwise rotation.
 
'''right:''' counter-clockwise rotation.
 
==MyMechanicalSourceComponent==
This component is responsible for proving mechanical power to the system. As this functionality requires special treatment by the mechanical group, any component that wants to provide mechanical power needs to be derived from this base class.
 
Similar to the base definition of any mechanical component, the source component also provides a couple of definition fields shared across sources, in addition to the ones already provided by the base.
 
<syntaxhighlight lang="xml">
<Friction>1</Friction>
<MaxPowerOutput>10</MaxPowerOutput>
<Rotation>Left</Rotation>
</syntaxhighlight>
 
'''Friction:''' how much static friction this component provides. Default: 1.
 
'''MaxPowerOutput:''' maximal power this component can provide. Default: 5.
 
'''Rotation:''' direction of rotation for the source (can be either Left or Right). Used to compute rotation for all connected blocks. Default: Right.
 
==MyBlockWindmillComponent==
A windmill is a specialized form of a mechanical power source that takes altitude into account when it comes to deciding how much power will be provided. To be more precise, it is not the altitude itself that matters, but rather how tall above the underlying terrain it stands (it does not work if built underground). In order to not require players to build infinitely tall towers, it is capped at the height specified in the definition. Any changes to the terrain after the windmill is already built will not result in a recalculation of power provided. There is also no wind or any other factor that would cause the power to fluctuate over time.
 
'''Example:'''
 
<syntaxhighlight lang="xml" line>
<Definition xsi:type="MyObjectBuilder_BlockWindmillComponentDefinition">
  <Id Type="BlockWindmillComponent" Subtype="WindmillHuge" />
  <MaxPowerOutput>20</MaxPowerOutput>
  <MaxAltitudeDelta>42</MaxAltitudeDelta>
  <PowerInterpolation>QuadraticEaseOut</PowerInterpolation>
  <Connection>connection_01</Connection>
  <ClockwiseAnimation>cw</ClockwiseAnimation>
  <CounterClockwiseAnimation>ccw</CounterClockwiseAnimation>
</Definition>
</syntaxhighlight>
 
'''Id:''' the type for windmill is BlockWindmillComponent.
 
'''MaxAltitudeDelta:''' maximal distance in meters (1 large block is 2.5 meters tall) between the center of the block and the terrain directly below it. If the distance is greater than this number, the block will provide full power (MaxPowerOutput). If the distance is between 0 and the number specified, the power output is calculate using the method specified in PowerInterpolation. Default: 20.
 
'''PowerInterpolation:''' interpolation curve used to determine the power provided at the current distance from terrain. Default: Linear.
 
 
'''Valid interpolation methods:'''
{{Table-Start}}
{{Table-Cell|Linear}}
{{Table-Cell|QuadraticEaseIn}}
{{Table-Cell|QuadraticEaseOut}}
{{Table-AddRow}}
{{Table-Cell|QuadraticEaseInOut}}
{{Table-Cell|QuadraticEaseOutIn}}
{{Table-Cell|CubicEaseIn}}
{{Table-AddRow}}
{{Table-Cell|CubicEaseOut}}
{{Table-Cell|CubicEaseInOut}}
{{Table-Cell|CubicEaseOutIn}}
{{Table-AddRow}}
{{Table-Cell|QuarticEaseIn}}
{{Table-Cell|QuarticEaseOut}}
{{Table-Cell|QuarticEaseInOut}}
{{Table-AddRow}}
{{Table-Cell|QuarticEaseOutIn}}
{{Table-Cell|QuinticEaseIn}}
{{Table-Cell|QuinticEaseOut}}
{{Table-AddRow}}
{{Table-Cell|QuinticEaseInOut}}
{{Table-Cell|QuinticEaseOutIn}}
{{Table-Cell|SinusoidalEaseIn}}
{{Table-AddRow}}
{{Table-Cell|SinusoidalEaseOut}}
{{Table-Cell|SinusoidalEaseInOut}}
{{Table-Cell|SinusoidalEaseOutIn}}
{{Table-AddRow}}
{{Table-Cell|ExponentialEaseIn}}
{{Table-Cell|ExponentialEaseOut}}
{{Table-Cell|ExponentialEaseInOut}}
{{Table-AddRow}}
{{Table-Cell|ExponentialEaseOutIn}}
{{Table-Cell|CircularEaseIn}}
{{Table-Cell|CircularEaseOut}}
{{Table-AddRow}}
{{Table-Cell|CircularEaseInOut}}
{{Table-Cell|CircularEaseOutIn}}
{{Table-Cell|ElasticEaseIn}}
{{Table-AddRow}}
{{Table-Cell|ElasticEaseOut}}
{{Table-Cell|ElasticEaseInOut}}
{{Table-Cell|ElasticEaseOutIn}}
{{Table-AddRow}}
{{Table-Cell|BackEaseIn}}
{{Table-Cell|BackEaseOut}}
{{Table-Cell|BackEaseInOut}}
{{Table-AddRow}}
{{Table-Cell|BackEaseOutIn}}
{{Table-Cell|BounceEaseIn}}
{{Table-Cell|BounceEaseOut}}
{{Table-AddRow}}
{{Table-Cell|BounceEaseInOut}}
{{Table-Cell|BounceEaseOutIn}}
{{Table-Cell|}}
{{Table-End}}
 
We will not go into a detailed explanation of how each of them works, there are plenty of illustrations you can find on the internet, such as [http://hosted.zeh.com.br/tweener/docs/en-us/misc/transitions.html here].
 
==MyMechanicalTransmissionComponent==
Transmission is the simplest of the mechanical components because it provides no functionality above what the base mechanical component already does. There are also no additional definition fields to fill in. This also means there is no reason to create a child class of the transmission because the mechanical system only cares about how many transmissions are in a group, it does not require any other data they might provide.
 
==MyMechanicalSinkComponent==
Power sink is once again a very simple component that does not implement any difficult logic. The only thing it provides to the mechanical system is friction. However, the sink component has one other function and that is being a power provider to other, non-mechanical systems, such as crafting.
 
'''Example:'''
 
<syntaxhighlight lang="xml" line>
<Definition xsi:type="MyObjectBuilder_MechanicalSinkComponentDefinition">
  <Id Type="MechanicalSinkComponent" Subtype="MillstoneMechanical" />
  <Connection>connection_up</Connection>
  <Friction>1</Friction>
  <ClockwiseAnimation>cw</ClockwiseAnimation>
  <CounterClockwiseAnimation>ccw</CounterClockwiseAnimation>
</Definition>
</syntaxhighlight>
 
'''Id:''' sink is using the MechanicalSinkComponent type.
 
'''Friction:''' how much friction does the sink contribute to its mechanical group. Default: 1.
 
 
[[Category:Keen_Modding_Guides]]
[[Category:Keen_Modding_Guides]]

Latest revision as of 20:20, 18 July 2022



The mechanical system is a simple system that provides power to machinery, without having to rely on physics, and it also has very low performance impact.


Version: 0.6.1

MyMechanicalPowerSystem

The brain of the mechanical system, that is added to all grids. It holds all of the mechanical groups for the grid that it is attached to and ensures that when you register a new component (node) in the system, it gets added to the proper group or a new one is created for it.

Definition

<Definition xsi:type="MyObjectBuilder_MechanicalPowerSystemDefinition">
  <Id Type="MechanicalPowerSystem" Subtype="CubeGridLarge" />
  <TransmissionFrictionMultiplier>1.08</TransmissionFrictionMultiplier>
</Definition>

Id: mandatory field, type for mechanical system is MechanicalPowerSystem.

TransmissionFrictionMultiplier: the amount of multiplicative friction added per transmission block in a group.


NOTICE: This definition is only useful when you want to override its properties, since this component is really only useful on a grid, and all of them have this definition added by default.

Groups

A group represents a collection of interconnected mechanical components. It acts as a singular unit, meaning that anything that is computed for the group, is going to be applicable for any and all of the components it stands for.

Power Calculation

For a group to be considered functional, it needs to have more power than friction. This means for a group to be useful at all, it needs to contain at least one power source, for example, a windmill. Each time a new block is added to the group or a block is removed from the group, some of its properties need to be recalculated.


Power Formula:

RP = (P - F * M^N) / P;

P: Total power of all connected sources.

F: Total friction of all connected sources and sink.

M: Transmission friction multiplier.

N: Number of connected transmissions.

RP: Relative power.

If the calculation would result in a negative value, it is instead reset to 0, as the relative power only makes sense between 0 and 1. A group is considered to be powered when RP > 0. All numbers, with the exception of relative power, are integers.

Rotational Alignment

Mechanical power in the game is represented in the form of rotation. Any mechanical part, except for the power source, can spin in either direction. The possible directions are clockwise (also referred to as left), and counter-clockwise (also referred to as right). A mechanical source comes with a pre-determined rotation, which is used to calculate rotation on all other connected blocks. If there are more sources connected to one group of blocks, the calculation is done from each one of them, breadth-first. If the calculation encounters a problem, such as two blocks that are attached, but the rotations passed on from their sources do not match, the whole mechanical group will come to a halt until the issue is resolved. This can only be resolved by removing one of the touching blocks or the source that is providing the "wrong" rotation.

MyMechanicalComponentBase

Mechanical components are nodes in the group. MyMechanicalComponentBase is a base implementation that includes all of the common functionality, such as registering in the system, detecting connections, adding or removing connections, changing animations and providing events. It also provides some common definition fields.

Definition

The base component includes a couple of definition fields, that are shared across all mechanical blocks.

<Definition xsi:type="MyObjectBuilder_MechanicalTransmissionComponentDefinition">
  <Id Type="MechanicalTransmissionComponent" Subtype="TransmissionWood" />
  <Connection>connection_forward</Connection>
  <Connection>connection_backward</Connection>
  <ClockwiseAnimation>cw</ClockwiseAnimation>
  <CounterClockwiseAnimation>ccw</CounterClockwiseAnimation>
</Definition>

Connection: name of the model dummy, whose properties are supposed to be used for mechanical connections.

ClockwiseAnimation: name of the animation to be played when this block is considered to be rotating clockwise. The animation has to be defined in a SubpartAnimationComponent.

CounterClockwiseAnimation: name of the animation to be played when this block is considered to be rotating counter-clockwise. The animation has to be defined in a SubpartAnimationComponent. In most cases, the counter-clockwise animation is just a reversed clockwise animation.

Connections

As you may have noticed in the definition, we are using model dummies to specify the location of what we refer to as connection ports. There are two important factors to take into consideration when adding a port dummy to your model:

  • Position
  • Local Rotation

Position is fairly straightforward: if you want two blocks to be connectable via a mechanical system, their port dummies need to intersect. The code, that does this check, completely disregards the size of the dummy and only requires the centers of the two dummies to be as close to each other as possible. There is very little margin for error when checking the distance, in order to rule out false positives, so make sure to place your dummies as precisely as possible. As was already mentioned, the size of the dummy does not play a role, so set it to something that makes it comfortable to work with in your editor of choice.

Local Rotation is a little more tricky - it is not actually the whole rotation matrix that the game is interested in. We only care about the local x-axis of the dummy. The connection check looks at the local axes of the two intersecting dummies, and if their x-axes are running in parallel, a connection can be made. But now comes the actual tricky part - if both axes are running in the same direction, the connection is considered to be a direct connection. If the two axes are running opposite to each other, the connection is inverted. In case the axes are not parallel at all, a connection cannot be made.

While both connection types (direct and inverted) are valid and will transfer power, it is important to watch the rotation when adding dummies to the block. The orientation of the port dummy is never absolute, it is always considered in relation to the other ports on the block. Is it very important to have it done correctly so that you can set your animations accordingly. It helps a lot if you think of the ports as input and output, and then try to visualize how you want each part to rotate, using pen and paper can help a lot in this step. You will find a simple rotation table below, which shows how the rotation works for a block with two ports. It is pretty much the same for any amount of ports, you just need to do a lot more drawing.

Input port orientation Input port rotation Output port orientation Output port rotation
in left out left
in left in right
in right out right
in right in left
Out left in left
Out left out right
Out right in right
Out right out left

in: the local x-axis is pointing into the block space.

out: the local x-axis is pointing out of the block space.

left: clockwise rotation.

right: counter-clockwise rotation.

MyMechanicalSourceComponent

This component is responsible for proving mechanical power to the system. As this functionality requires special treatment by the mechanical group, any component that wants to provide mechanical power needs to be derived from this base class.

Similar to the base definition of any mechanical component, the source component also provides a couple of definition fields shared across sources, in addition to the ones already provided by the base.

<Friction>1</Friction>
<MaxPowerOutput>10</MaxPowerOutput>
<Rotation>Left</Rotation>

Friction: how much static friction this component provides. Default: 1.

MaxPowerOutput: maximal power this component can provide. Default: 5.

Rotation: direction of rotation for the source (can be either Left or Right). Used to compute rotation for all connected blocks. Default: Right.

MyBlockWindmillComponent

A windmill is a specialized form of a mechanical power source that takes altitude into account when it comes to deciding how much power will be provided. To be more precise, it is not the altitude itself that matters, but rather how tall above the underlying terrain it stands (it does not work if built underground). In order to not require players to build infinitely tall towers, it is capped at the height specified in the definition. Any changes to the terrain after the windmill is already built will not result in a recalculation of power provided. There is also no wind or any other factor that would cause the power to fluctuate over time.

Example:

<Definition xsi:type="MyObjectBuilder_BlockWindmillComponentDefinition">
  <Id Type="BlockWindmillComponent" Subtype="WindmillHuge" />
  <MaxPowerOutput>20</MaxPowerOutput>
  <MaxAltitudeDelta>42</MaxAltitudeDelta>
  <PowerInterpolation>QuadraticEaseOut</PowerInterpolation>
  <Connection>connection_01</Connection>
  <ClockwiseAnimation>cw</ClockwiseAnimation>
  <CounterClockwiseAnimation>ccw</CounterClockwiseAnimation>
</Definition>

Id: the type for windmill is BlockWindmillComponent.

MaxAltitudeDelta: maximal distance in meters (1 large block is 2.5 meters tall) between the center of the block and the terrain directly below it. If the distance is greater than this number, the block will provide full power (MaxPowerOutput). If the distance is between 0 and the number specified, the power output is calculate using the method specified in PowerInterpolation. Default: 20.

PowerInterpolation: interpolation curve used to determine the power provided at the current distance from terrain. Default: Linear.


Valid interpolation methods:

Linear QuadraticEaseIn QuadraticEaseOut
QuadraticEaseInOut QuadraticEaseOutIn CubicEaseIn
CubicEaseOut CubicEaseInOut CubicEaseOutIn
QuarticEaseIn QuarticEaseOut QuarticEaseInOut
QuarticEaseOutIn QuinticEaseIn QuinticEaseOut
QuinticEaseInOut QuinticEaseOutIn SinusoidalEaseIn
SinusoidalEaseOut SinusoidalEaseInOut SinusoidalEaseOutIn
ExponentialEaseIn ExponentialEaseOut ExponentialEaseInOut
ExponentialEaseOutIn CircularEaseIn CircularEaseOut
CircularEaseInOut CircularEaseOutIn ElasticEaseIn
ElasticEaseOut ElasticEaseInOut ElasticEaseOutIn
BackEaseIn BackEaseOut BackEaseInOut
BackEaseOutIn BounceEaseIn BounceEaseOut
BounceEaseInOut BounceEaseOutIn

We will not go into a detailed explanation of how each of them works, there are plenty of illustrations you can find on the internet, such as here.

MyMechanicalTransmissionComponent

Transmission is the simplest of the mechanical components because it provides no functionality above what the base mechanical component already does. There are also no additional definition fields to fill in. This also means there is no reason to create a child class of the transmission because the mechanical system only cares about how many transmissions are in a group, it does not require any other data they might provide.

MyMechanicalSinkComponent

Power sink is once again a very simple component that does not implement any difficult logic. The only thing it provides to the mechanical system is friction. However, the sink component has one other function and that is being a power provider to other, non-mechanical systems, such as crafting.

Example:

<Definition xsi:type="MyObjectBuilder_MechanicalSinkComponentDefinition">
  <Id Type="MechanicalSinkComponent" Subtype="MillstoneMechanical" />
  <Connection>connection_up</Connection>
  <Friction>1</Friction>
  <ClockwiseAnimation>cw</ClockwiseAnimation>
  <CounterClockwiseAnimation>ccw</CounterClockwiseAnimation>
</Definition>

Id: sink is using the MechanicalSinkComponent type.

Friction: how much friction does the sink contribute to its mechanical group. Default: 1.