A collection of scripts have been created in order to procedurally generate a Spider using primitives, exposing various parameters in order to configure its body size, number of legs, legs IK and legs movement animation.
The legs are made of three parts/bones: UpperLeg, MiddleLeg and LowerLeg (representing the joints/pivots). The UpperLeg is attached to the body, and the LowerLeg is looking towards the IK target, ideally oriented parallel to the normal of the surface point targeted.
The hierarchy of the generated Spider GameObject should look like this (pseudo-JSON):
The elevation and rotation of the main body is done by rotating the Spine based on the mean position determined by the left and right legs, with respect to the mean up vector of each leg IK target (represented by the normal of the surface in each target point) for elevation, and with respect to the up vector resulted as a cross product between the vector from the left legs to the right legs and the forward vector of the main Spider object.
The joint between UpperLeg and MiddleLeg is rotated towards a hint represented by the UpperLeg's position translated by its up vector (although this might not be the best hint, but I tried to keep it simple). This is done by creating a plane having its origin in UpperLeg's position and the normal defined by LowerLeg-UpperLeg, then MiddleLeg and the hint are projected on the plane in order to determine the shortest angle between them and rotate the MiddleLeg joint around the plane's normal accordingly.
The following scripts were created:
SpiderGenerator - creates the Spider hierarchy, attaches other scripts, initializes them
SpiderController - handles the elevation, rotation and "base movement" of the main body (moving up and down along its up vector, can represent the idle animation as well)
Legs/
SpiderLegIK - responsible for the Inverse Kinematics logic of a leg. It uses references to the UpperLeg, MiddleLeg and LowerLeg in order to move and constraint the joints while trying to reach for the given target. A target is defined by the SpiderLegIKTarget structure, containing Position and Normal vectors of the target the leg should reach.
SpiderLegsController - responsible for moving all the Spider's legs. When the distance between a leg and its target is greater than minLegDistance (the minLegDistanceToMove of the SpiderGenerator script), the leg must be moved to its target's new position. Each frame, the script grounds the targets properly casting a ray from above the target while temporarily moving the legs to the "Ignore Raycast" layer (layer 2) and updating the position to the hit point. The legs are then moved in a zig-zag pattern, starting with the [0, 3, 4, 7, ...] chain of legs, interpolating their IK Target position (the SpiderLegIKTarget structure type property) between the previous target position and the next target position using a Coroutine. Only after all these legs finished moving, the other legs are then moved using the same interpolation method, regardless of their distance to their target.
In order to use this functionality, an empty GameObject must be created and the SpiderGenerator script should be attached to it, configuring the following properties:
bodyScale - Vector3 representing the localScale of the main-body mesh (Spine.Cube from the previously mentioned hierarchy).
upperLegLength, middleLegLength, lowerLegLength - the lengths of the leg's sections; their sum should be greater than minLegDistanceToMove * spaceBetweenTargetsScale. They also influence the Spider's height (body's offset from the ground along the up vector), because it's set to half of the total length of a leg ((upperLegLength + middleLegLength + lowerLegLength) / 2)
legsWidth - the width of each leg segment (it sets their x and y local scales)
ikDelta - the accepted error (distance between a leg and its target) under which the IK computation can stop for the frame it's running
ikIterations - the maximum number of iterations for IK computations in a frame
legsPairCount - the number of pairs of legs the Spider should have. The legs are spaced evenly based on the main-body's length (bodyScale.z)
minLegDistanceToMove - the minimum distance between a leg and it's target needed to trigger the update of its new target position
legsRaiseHeight - the amount of elevation to be applied to a leg during its IK position interpolation
timeToMoveLeg - the duration in seconds of the movement of a leg (how fast it moves to its new target)
spaceBetweenTargetsScale - the scale to multiply the body's length (bodyScale.z) by, in order to "spread" the leg targets
xDistanceFromBodyToTargets - the distance between the main-body and the leg targets (how far from the body are the attached targets created)
baseAnimationHeightOffset - the range in which the Spider's main-body moves up and down along it's up vector (the "base animation". This value is multiplied by the Spider's height (and divided by 10) in order to compute the main-body offset during each frame. The position is set to spineBasePosition + spine.up * (Mathf.Sin(Time.time * 2) * baseAnimationHeightOffset * height / 10) each frame
Currently, the SpiderGenerator relies on pre-configuring the mentioned fields either using default values in the script or setting them from the editor. In order to better support the procedural generation idea of the game (so it can be used/triggered by another script, for example), the values could be set by reading a config file during the CreateSpider() method, or by creating a Init() method (similarly to how the other added scripts work) which acts like a constructor (taking the mentioned fields as required arguments) and executing the CreateSpider() method after the fields are set.
Release Notes: A new procedural Spider prototype have been added (#3).
Addressed Issue
Primitive characters (#3)
Description of the change
A collection of scripts have been created in order to procedurally generate a Spider using primitives, exposing various parameters in order to configure its body size, number of legs, legs IK and legs movement animation. The legs are made of three parts/bones:
UpperLeg
,MiddleLeg
andLowerLeg
(representing the joints/pivots). TheUpperLeg
is attached to the body, and theLowerLeg
is looking towards the IK target, ideally oriented parallel to the normal of the surface point targeted.The hierarchy of the generated Spider GameObject should look like this (pseudo-JSON):
The elevation and rotation of the main body is done by rotating the
Spine
based on the mean position determined by the left and right legs, with respect to the meanup
vector of each leg IK target (represented by the normal of the surface in each target point) for elevation, and with respect to theup
vector resulted as a cross product between the vector from the left legs to the right legs and theforward
vector of the main Spider object.The joint between
UpperLeg
andMiddleLeg
is rotated towards a hint represented by theUpperLeg
's position translated by itsup
vector (although this might not be the best hint, but I tried to keep it simple). This is done by creating a plane having its origin inUpperLeg
's position and the normal defined byLowerLeg-UpperLeg
, thenMiddleLeg
and the hint are projected on the plane in order to determine the shortest angle between them and rotate theMiddleLeg
joint around the plane's normal accordingly.The following scripts were created:
up
vector, can represent the idle animation as well)UpperLeg
,MiddleLeg
andLowerLeg
in order to move and constraint the joints while trying to reach for the given target. A target is defined by theSpiderLegIKTarget
structure, containingPosition
andNormal
vectors of the target the leg should reach.minLegDistance
(theminLegDistanceToMove
of theSpiderGenerator
script), the leg must be moved to its target's new position. Each frame, the script grounds the targets properly casting a ray from above the target while temporarily moving the legs to the "Ignore Raycast" layer (layer 2) and updating the position to the hit point. The legs are then moved in a zig-zag pattern, starting with the [0, 3, 4, 7, ...] chain of legs, interpolating their IK Target position (theSpiderLegIKTarget
structure type property) between the previous target position and the next target position using a Coroutine. Only after all these legs finished moving, the other legs are then moved using the same interpolation method, regardless of their distance to their target.In order to use this functionality, an empty GameObject must be created and the
SpiderGenerator
script should be attached to it, configuring the following properties:Spine.Cube
from the previously mentioned hierarchy).minLegDistanceToMove * spaceBetweenTargetsScale
. They also influence the Spider's height (body's offset from the ground along theup
vector), because it's set to half of the total length of a leg ((upperLegLength + middleLegLength + lowerLegLength) / 2
)x
andy
local scales)bodyScale.z
)bodyScale.z
) by, in order to "spread" the leg targetsup
vector (the "base animation". This value is multiplied by the Spider's height (and divided by 10) in order to compute the main-body offset during each frame. The position is set tospineBasePosition + spine.up * (Mathf.Sin(Time.time * 2) * baseAnimationHeightOffset * height / 10)
each frameCurrently, the
SpiderGenerator
relies on pre-configuring the mentioned fields either using default values in the script or setting them from the editor. In order to better support the procedural generation idea of the game (so it can be used/triggered by another script, for example), the values could be set by reading a config file during theCreateSpider()
method, or by creating aInit()
method (similarly to how the other added scripts work) which acts like a constructor (taking the mentioned fields as required arguments) and executing theCreateSpider()
method after the fields are set.Release Notes: A new procedural Spider prototype have been added (#3).
Preview