script_directories.blend
should be in game's root folder.
Contains script which runs 1st on runtime and adds the path to the Scripts
folder to current .blend file path.
Import initScriptDirectories
object from script_directories.blend
into any .blend file that you want to access the scripts
folder from.
Set Logic brick set to module
.
Value field in logic brick: scriptName.functionName
Set Debug
button to live reload external script on change.
No need to import the script into the internal Text Editor.
Disable Debug
when exporting final game.
Each object has a set of states for all its Controllers
Functions like calling a component and passing values to it, but as Logic bricks.
Without using messages all separate objects' sensors & actuators end up all bunched together like a giant script, rather than each object or group being a separate component whose logic is easy to follow.
Items are any collectable in the game.
Items are specified in the props.blend
file and linked to the playable game .blend
file.
An item object must have a near
sensor that detects when a main character is near, and triggers the item
script.
The item script only sends a message to the Inventory object if the item source object has an item
property.
The inventory
object only counts the item if the item property value and/or the instance parent's name is a property on the inventory
object.
The near sensor calls the collectItem
function defined in /scripts/general/items
.
Item must have boolean property collected
.
This collectItem
script sets this to 1
the specify the item as having been collected.
The collectItem
script sets the collected
property on any member object of item group to 1
.
An item must be an instanced group to work in game, ie. to be counted on inventory
. The collectItem
script checks for item object that called the script being part of an instance group, via item.groupObject
.
Items can be identified by any of the 3 following ways:
- Default
item
property on source object item
property on instance object- Group item instances
The inventory
object keeps any of these 3 that are assigned to an item.
Eg. Instance is added to group timepostdoor4
, item
property is assigned to instance with value timepost4
, & source object has item
property timepost
.
The inventory object will create a property & increment the count of each of these 3 properties -(timepostdoor4
, timepost4
, timepost
) everytime a timepost is collected.
The default item
property on the original source item. If this property is missing the item is identified by the name of its gameObject. Eg. item property: timepost. This allows multiple item objects to count as the same item.
Specified on the MessageToCountItem
actuator.
Eg. different health item objects with different designs that each add different amounts of health.
Add an item
property to the instance object.
Specified on the MessageToCountItemInstance
actuator.
Eg. 3 timepost instances have an item
property added & set to timepostdoor3
. Each time the timepost
item is collected, the timepostdoor3
property on the inventory
object is incremented by 1.
Add the of the item's group to their own group. This is preferred to #2 as the items are identified by their group name, which is unique across all scenes.
Specified on the MessageToCountItemGroup
actuator.
This prevents the error such as Eg. assigning 3 'timepost' items's item
property to timepostdoor6
, which triggers 'door4' to open when collected. But timepostdoor6
was assigned in a previous level & 3 items collected already, thus the door defaults to open because of this id conflict. Grouping the item instances prevents this.
The item object's name is used to identify the item in logic bricks, therefore if the item name is changed the in the Outliner the name must be changed in the item's relevant logic bricks also.
The object's name is used as the item ID to provide a simple way for ensuring that the item ID is always unique across all scenes in the .blend file.
Any scripts run on the source group objects to all instances & original object.
To apply to the individual instanced group that called the script, scripts on source objects can select the instanced object via owner.groupObject
and owner.groupObject.groupMembers
.
Inventory object is a child of the Character object.
This ensures that all inventory counts are transferred between levels, once the same character group is transferred.
The inventory
script checks for item counts in two ways:
For counting
~~
The item count is tracked in the inventory
object, which is in props.blend
This object must be linked within any .blend file where items are being collected.
The instanced inventory
group must have the properties specified on it that are to be counted.
The subject of the message sent to the Inventory object must be the same as a property on the object. ~~
Standard items. The number of items collected is tracked on the inventory
object. Eg. ammo.
When an item object is linked from props.blend
to the playable .blend file, if the linked item is added to a group, it will be counted as a group on the inventory
object.
Eg. The individual timepost
item can be added to a group named timepostsForDoor1
, which specifies 3 timeposts which must be collected to open door 1
.
These number of timeposts in the group timepostsForDoor1
would be tracked in the inventory
object. When the count reaches 3 the door1
open animation would be played.
The timepost group has to be instanced for it to work, as the instance's groupObject
's members are checked for the collected
property.
cameraRotateLeftRight
is vertex parented to character, so it doesn't get character's rotation, only translation.
Activated on left mouse button press.
Position of horizon convergence point from crosshair is calculated.
Horizon convergence point depends on how high or low your view is.
Lower view has smaller convergence point (can't see as far)
Higher view has farther point (can see further)
Taking horizon convergence point from standard 6 ft eye level, it's a distance of 3.1 miles, or 16368 ft. Have to convert this to blender units.
Full auto weapons: 10 rounds per second. Same as ak47 rifle
Semi auto: 2 rounds per second.
Using Radar sensor to define cone that specifies area in which enemy can detect player.
Move through defined patrol waypoints in order that they're listed.
Chase player when spotted. If player gets far enough away then go back to patrol.
The waypoints
can be any object that's a child of the linked enemy instance. The name of the object must start with waypoint
. The enemy moves to each waypoint according to the order in which they're arranged in the Outliner.
Seek out player whereever he is on map. Make it intermittent, unless player is within line of sight. Stop and take breaks, like switching to guard state momentarily.
Stay in one spot & turn side to side.
Chase player when spotted. If player gets far enough away then go back to patrol.
Properties that uniquely identify each enemy, like enemy's current life
, can be defined on each instance, or can be left as the default property as defined on the source of that instance.
Eg. default life
property on Patroller
enemies is 30
.
Only an instance of the original enemy object works, as the init
function assumes that it's running on an instance, ie. owner.groupObject != None
Code setup to only work with instances of the spawn point
Optional parameters:
- max: max number of enemies to spawn. Defaults to 15
- interval: time in seconds between spawning enemies. Defaults to 120
empty scene: balanced 60fps
pulse true on, 1 obj moving 0.05 in x axis every logic tic
balance: 60fps
start: 22fps low: 19fps balanced out after 15 secs: 20fps
ditto above
objects moving 0.05 on x axis, 2x per second, rather than 60x per second. constant 60fps
w/ pulse on, skip 4 frames, tap on, move 0.2 in x axis, or 4x amount, so object moves same amount per second, but in larger chunks every 4 logic tics
Appears slightly less smooth
constant 41fps
always sensor obj1 connected to property actuator that sets property to 1 on every logic tic on obj2
balanced: 59
2k objects. Always sensor pulse true on obj1, connected to python script on obj2 that sets activates motion actuator on obj2 every logic tic
start: 16
low: 12
balanced: 13
2k objects. Always sensor pulse false on obj1, connected to python script on obj2 that activates motion actuator on first logic tic only. Script doesn't fire after
start: 22fps
low: 19fps
balanced out after 15 secs: 20fps
Same as logic brick only setup w Always sensor pulse set to true to run on each logic tic.
2k objects. Always sensor pulse true on obj1, connected to python script on obj2 that activates motion actuator on every logic tic, on each tic checks property that has it only only activate actuator on 1st logic tic
start: 16
low: 12
balanced: 13
Just running script every logic tic has big hit on framerate regardless.
2000 objects: message sensor connected to motion actuator that moves obj 0.05 each logic tic on x axis, always sensor connected message actuator on obj1
always sensor positive pulse MUST be on for object to move on each logic tic. Message sensor doens't need positive pulse
This setup results in ~ 3 to 4 fps less than two directly connected objects for 2000 objects. .4fps for 200 objects. And that's if they're all firing at the same time!
balanced: 57fps
start: 17fps
low: 13fps
balanced: 14fps
ditto above
start: 17fps
low: 13fps
balanced: 14fps
Basically no difference, just have to contend with possible naming conflicts between other broadcasted messages.
always sensor & message sensor set to skip 4 frames & tap on. Motion set to 0.2 in x axis. Same amount of movement, but larger increments every 4 logic tics
start: 39
low: 34
balanced: 37
2k objects. On obj1 Always sensor w pulse off, sends message to obj2. Obj 2 message sensor pulse off, sets property on obj2. Property sensor w pulse off triggers motion actuator on object every logic tic
low: 11
balanced: 12.5
Why? Every additional sensor equates to more checks to run each logic tic. The message sensor
2000 objects: always sensor connected message actuator on obj1, message sensor connected to property actuator that sets property to 1 on every logic tic
balanced: 57
balanced: 51
always & message sensor +ve pulse on, only send message on 1st logic tic
balanced: 58
Pulse mode on/off makes no difference if controller is activated each logic tic.
less sensors the better regardless of the setup.
sensor.active
deactivating unused sensors via python script should increase performance
OR use states, within which ONLY the sensors that are needed for the object's current functionality will be active.
Unneeded message sensors as opposed to direct connection will start to add up.
Direct connections between objects faster than message sensors
but requires 100s of messages at same time to affect fps on my system.
So minimise but use where relevant such as between separate Collections/groups.
SKIPPING logic tics on sensors can make a significant difference in performance
Auto generated Table of Contents with VSCode Plugin Auto Markdown TO
Install plugin in VSCode
In .md file, Right click -> "Auto Markdown TOC Insert/Update TOC"
On saving file TOC is auto updated