[bb] Triggerplate system for detecting intrusion events by Zethrax [ 1+ years ago ]

Started by BlitzBot, June 29, 2017, 00:28:39

Previous topic - Next topic

BlitzBot

Title : Triggerplate system for detecting intrusion events
Author : Zethrax
Posted : 1+ years ago

Description : A simple system for using the Blitz3D collision system to trigger events when a trigger entity passes through a triggerplate quad. Can be used for visibility occlusion management, spawning items and enemies, etc. Also includes a simple visibility management system in the example.

; A simple system for using the Blitz3D collision system to trigger events when a trigger entity passes through a triggerplate quad. Can be used for visibility occlusion management, spawning items and enemies, etc. Also includes a simple visibility management system in the example.

; See the function comments for detailed information.


; USAGE:-

; Call 'SetupTriggerplates' after using the 'Graphics3D' command to set the graphics mode and before using 'CreateTriggerplate'. This only needs to be run the once unless you destroy the graphics mode.

; Use 'CreateTriggerplate' to create your triggerplates. Position and align the triggerplate quad using the returned entity handle.

; Load, position, and align all your game match entities.

; Use 'CreateTrigger' to create your triggers and assign them to any game match entities that will move from room to room, in particular your player entity.

; Use 'ResetTriggers' to initialize the triggers before starting the match.

; Place 'UpdateTriggers' in your main program loop to update trigger events.

; Your main program should have a function called 'DoTriggeredAction( trigger, triggerplate )' to handle trigger events. This function should use 'EntityName( trigger )' and 'EntityName( triggerplate )' to extract the identifier strings assigned when the colliding trigger and triggerplate were created. These identifiers can be used to select the actions to take to process the trigger event.

; When the match is over call 'DestroyTriggerObjects' to desroy the type objects used to store the trigger handles.



; GENERAL NOTES:-

; When teleporting an entity with a trigger attatched, call 'ResetEntity <trigger>' after positioning it, where '<trigger>' is the handle of the trigger. You may also want to do this after resizing the collision sphere of your entity during gameplay.



; VISIBILITY MANAGEMENT NOTES:-

; When using 'EntityAlpha entity, 0.0' and 'EntityAlpha entity, 1.0' to turn the rendering of an entity off and on for visibility management purposes, you can set the alpha level of an entity that has mesh data so that the mesh entity's alpha level setting is restored when rendering is turned on again. To do this, create a brush; set the alpha level of the brush to whatever you want it to be; paint the mesh with the brush using 'PaintMesh'; free the brush (unless you want to use it again). Since the entity and the mesh data it references contain separate sets of visible attributes, the alpha level set using 'PaintMesh' will be unaffected when the entity's rendering is turned on and off using 'EntityAlpha'. Note that mesh entities that are copied using 'CopyEntity' will all need to have the same mesh alpha setting (the alpha setting set using 'PaintMesh) as their visible properties are all referenced from the same set of data. Use 'CopyMesh' if the visible mesh properties need to be different for each copy.

; Something to be aware of when doing visibility management is that a scene consisting of a collection of entities may include some entities which do not have visible properties. Attempting to use the 'EntityAlpha' command on entities which do not have visible properties will result in an error condition. One method for dealing with this is described below:-

; This method assumes that entities will have an integer id stored in their namestring to link the entity with data associated with it, and that all valid ids will be positive and non-zero. Use a positive id for entities that need to have their visibility managed (and that consequenly have visible properties that can be modified), and a negative id for other entities. A zero value id (which will be returned if the namestring is empty or if the first character is not a valid character used with integer literals) should indicate that there is no data associated with the entity. When recursing the entity structure of a scene for visibility management purposes, use the 'Sgn' function to test if the id stored in an entity's namestring has a positive value before setting its renderstate using 'EntityAlpha'. When using the stored id to index the data associated with an entity, use the 'Abs' function to convert all the non-zero ids to a positive value. Note that 'Sgn' and 'Abs' only work on numeric values, so use 'Int' to convert the ids from strings to integers, where necessary.



Const C_COLLTYPE_TRIGGER = 1 ; The collision type used for trigger entities. Change this to suit your game.
Const C_COLLTYPE_TRIGGERPLATE = 2 ; The collision type used for triggerplate entities. Change this to suit your game.



Type T_trigger
Field trigger
End Type



Global G_triggerplate_source



Function CreateQuad( parent = 0 )
; Creates a quad mesh. The quad is aligned to the global dimensional axiis with its frontface facing in the direction of the positive z axis.
; The quad has a width and height of one world unit, and its centerpoint is at the center of the quad.
; The normals for the quad are not updated as the quad may form part of a larger mesh. The normals should be set by the calling routine.

Local quad, surface, v0, v1, v2, v3

quad = CreateMesh ( parent )
; Create a surface for this quad.
surface = CreateSurface( quad )
; Create corner vertices for quad.
v0 = AddVertex( surface, 0.5, 0.5, 0.0, 0.0, 0.0 );top left
v1 = AddVertex( surface, -0.5, 0.5, 0.0, 1.0, 0.0 );top right
v2 = AddVertex( surface, -0.5, -0.5, 0.0, 1.0, 1.0 );bottom right
v3 = AddVertex( surface, 0.5, -0.5, 0.0, 0.0, 1.0 );bottom left
; Create 2 triangles to create quad.
AddTriangle surface, v0, v1, v2
AddTriangle surface, v0, v2, v3

Return quad
End Function



Function SetupTriggerplates()
; Creates a triggerplate source entity. All the triggerplates will instance this entity.
; The triggerplate consists of a single quad.
; The triggerplate is aligned to the global dimensional axiis with its frontface facing in the direction of the positive z axis.
; The triggerplate has a width and height of one world unit, and its centerpoint is at the world center.
; This function should be called after the Graphics3D mode has been set up and before 'CreateTriggerplate' is used. It should only need to be called once, when the program is run.

G_triggerplate_source = CreateQuad()
UpdateNormals( G_triggerplate_source )
Local brush = CreateBrush ()
BrushAlpha brush, 0.0
PaintMesh G_triggerplate_source, brush
FreeBrush brush
EntityType G_triggerplate_source, C_COLLTYPE_TRIGGERPLATE
Collisions C_COLLTYPE_TRIGGER, C_COLLTYPE_TRIGGERPLATE, 2, 1
HideEntity G_triggerplate_source
End Function



Function CreateTriggerplate( identifier$, width# = 1.0, height# = 1.0, parent = 0 )
; Creates a triggerplate.
; A triggerplate is a single sided quad which generates a collision event when a trigger created with 'CreateTrigger' comes into contact with it. The trigger event is processed by 'UpdateTriggers'.
; 'identifier$' will be placed into the namestring of the triggerplate entity and should contain the text value you wish to use to identify the appropriate action to take when this triggerplate is triggered.
; 'width#' and 'height#' are used to optionally set the width and height of the triggerplate.
; 'parent' is optionally used to set the parent of the triggerplate.
; The entity handle of the triggerplate is returned. You can use this handle in combination with the entity position and rotation commands to set the location and alignment of the trigger.
; You'll usually parent your triggerplate to the main room model for the room that you want to trigger events for. You'll position and align it so that it is at an entrance to the room, and is facing out of the room and towards another triggerplate parented to the adjoining room that the entrance connects to. The two triggerplates facing each other from different rooms will form a sandwich shaped trap to capture the triggers created with 'CreateTrigger'. The triggerplates should be positioned so that their frontfaces face each other and are separated by a distance of at least 0.1 world units.

Local triggerplate = CopyEntity( G_triggerplate_source, parent )
ScaleEntity triggerplate, width#, height#, 1.0, True
NameEntity triggerplate, identifier$
Return triggerplate
End Function



Function CreateTrigger( identifier$, parent = 0 )
; Creates a trigger.
; A trigger is an entity with a source collision sphere, which generates a collision event when it comes into contact with a triggerplate created with 'CreateTriggerplate'. The trigger event is processed by 'UpdateTriggers'.
; 'identifier$' will be placed into the namestring of the trigger entity and should contain the text value you wish to use to identify the appropriate action to take when this trigger generates a collision.
; Triggers have a diameter of 0.08 world units (radius 0.04). Triggerplates placed in a 'sandwich' arrangement should be placed so that the distance between them is greater than the diameter of the triggers (a distance of 0.1 is recommended).
; The entity handle of the trigger is returned. You not need to make any additional changes to the trigger.

Local trigger = CreatePivot( parent ) ; Creates the pivot used as the trigger. If you want to use 'EntityAlpha' to do visibility management you may want to use 'CreateMesh()' here instead of 'CreatePivot()'.
EntityRadius trigger, 0.04 ; Gives the trigger a diameter of 0.08 world units.
EntityType trigger, C_COLLTYPE_TRIGGER
NameEntity trigger, identifier$
Local trigger_object.T_trigger = New T_trigger
trigger_object rigger = trigger

Return trigger
End Function



Function UpdateTriggers()
; Checks for and processes collision events generated when a trigger created with 'CreateTrigger' comes into contact with a triggerplate created with 'CreateTriggerplate'.
; This function expects the main program to supply a function called 'DoTriggeredAction'. 'DoTriggeredAction' should expect two integer parameters to be passed.  It should expect the entity handle of the colliding trigger as its first parameter, and the entity handle of the triggerplate collided with as its second parameter. The entityname of the trigger and triggerplate entities can then be parsed for the 'identifier$' values set in 'CreateTrigger' and 'CreateTriggerplate', which can be used to select the action you wish to take to process the trigger event.

Local trigger_object.T_trigger, triggerplate
For trigger_object = Each T_trigger
triggerplate =  EntityCollided ( trigger_object rigger, C_COLLTYPE_TRIGGERPLATE )
If triggerplate
PositionEntity trigger_object rigger, 0.0, 0.0, 0.0 ; Position the trigger entity at the center of it's parent's local space.
ResetEntity trigger_object rigger ; Reset the collision state of the trigger to allow it to pass through the triggerplate.
DoTriggeredAction trigger_object rigger, triggerplate ; The function 'DoTriggeredAction' should be provided by the main program. It should expect the entity handle of the colliding trigger as its first parameter, and the entity handle of the triggerplate collided with as its second parameter. Both of these parameters should be integers.
EndIf
Next
End Function



Function ResetTriggers()
; Resets the collision data and local positions of all of the triggers.
; Call this before starting a match, but after you have positioned all the match entities.

Local trigger_object.T_trigger
For trigger_object = Each T_trigger
PositionEntity trigger_object rigger, 0.0, 0.0, 0.0 ; Position the trigger entity at the center of it's parent's local space.
ResetEntity trigger_object rigger ; Reset the collision state of the trigger.
Next
End Function



Function DestroyTriggerObjects()
; Destroys all of the trigger objects used with triggers.
; This function should be called when a game match has ended and you need to clean up the match data in preparation for a new match. If an existing match is being replayed it will probably not be necessary to call this function, as the objects may be reused.
; Note that it should not be necessary to explicitly destroy the trigger and triggerplate entities, as they will normally be parented to other entities and will be automatically destroyed in the normal course of freeing up a game match.

Delete Each T_trigger
End Function



; === End of trigger lib ===



;---------------------------------------------------------------------



; ==== Example code ====

Global G_app_name$ = "Game Name"

AppTitle G_app_name$


Const C_COLLTYPE_PLAYER = 3
Const C_COLLTYPE_ARENA = 4



Const C_NUM_ROOMS = 5
Dim A_room( C_NUM_ROOMS )

Graphics3D 800, 600, 0, 2
Global G_app_handle = SystemProperty( "" )
SetBuffer BackBuffer()

Global G_light = CreateLight()

SetupTriggerplates ; Set up the triggerplate system. This must be placed after the Graphics3D mode has been set up, but before 'CreateTriggerplate' is used.


; -- Create the arena.
CreateArena
;^^^^^^


; -- Create the player and camera.
Global G_player = CreateSphere( 16 )
UpdateNormals G_player
EntityColor G_player, 200.0, 200.0, 0.0
MoveEntity G_player, 0.0, 1.0, 0.0
EntityRadius G_player, 1.0
EntityType G_player, C_COLLTYPE_PLAYER
Collisions C_COLLTYPE_PLAYER, C_COLLTYPE_ARENA, 2, 2

Global G_player_trigger = CreateTrigger( "Player", G_player ) ; Create the player's trigger. Note that the namestring will have a value of zero when converted to an integer, so no attempt will be made by the visibility management system to modify its visibility.

Global G_camera = CreateCamera( G_player ) ; Create the camera. Note that the camera's entity string is empty, so no attempt will be made by the visibility management system to modify its visibility as the id obtained from the entity string will be a zero value.
CameraZoom G_camera, 1.6
TurnEntity G_camera, 90.0, 0.0, 0.0
MoveEntity G_camera, 0.0, 0.0, -40.0
;^^^^^^


Global G_timer = CreateTimer( 30 )

Global G_player_speed# = 0.4


Global G_old_room
Global G_current_room

Global G_message1$, G_message2$



HideAllRooms
ShowRoom( 2 )
ShowRoom( 3 )
ShowRoom( 4 )
G_current_room = 3

ResetTriggers ; Reset the triggers. Call this before starting a match, but after you have positioned all the match entities.

RunGame

End



Function RunGame()

Local movex#, movez#

Repeat

UpdateTriggers

movex# = 0.0
movez# = 0.0

; -- Get player input.
If KeyDown( 205 ) Or KeyDown( 32 )
movex# = G_player_speed#
ElseIf KeyDown( 203 ) Or KeyDown( 30 )
movex# = -G_player_speed#
EndIf

If KeyDown( 200 ) Or KeyDown( 17 )
movez# = G_player_speed#
ElseIf KeyDown( 208 ) Or KeyDown( 31 )
movez# = -G_player_speed#
EndIf

MoveEntity G_player, movex#, 0.0, movez#
;^^^^^^

UpdateWorld
RenderWorld
Color 255, 255, 255
Text 10, 10, "Use W, A, S, D or the cursor keys to move. Press the ESCAPE key to exit."
Color 0, 200, 200
Text 10, 70, G_message1$
Color 200, 0, 0
Text 10, 90, G_message2$
Flip
WaitTimer G_timer
Until KeyHit( 1 )

End Function



Function DoTriggeredAction( trigger, triggerplate )
; Handle trigger events captured by 'UpdateTriggers'.
; The 'trigger' parameter will hold the entity handle of the trigger that triggered the event. The 'triggerplate' parameter will hold the entity handle of the triggerplate that was triggered.
; Use 'EntityName' to get the identifier strings that you set using 'CreateTrigger' and 'CreateTriggerplate'. These identifiers should be used to route to the action that you wish to take, and to supply data about that action.



Local trigger_identifier$ = EntityName( trigger )
Local action_id = EntityName( triggerplate )
Local parent = GetParent( triggerplate )

action_id = Abs( action_id ) ; Convert the id to a positive value in case it was set as a negative number so that it would be ignored by the visibility management system.

If action_id < 101 ; Check that it's a room id. Room id's have values from 1 to 100.

G_old_room = G_current_room
G_current_room = EntityName( parent )

G_message1$ = trigger_identifier$ + " has left room " + G_old_room + " and entered room " + G_current_room + "."

G_message2$ = ""

Select action_id

Case 1

EntityParent G_player, A_room( 1 )
HideAllRooms
ShowRoom( 1 )
ShowRoom( 2 )

Case 2

EntityParent G_player, A_room( 2 )
HideAllRooms
ShowRoom( 1 )
ShowRoom( 2 )
ShowRoom( 3 )

Case 3

EntityParent G_player, A_room( 2 )
HideAllRooms
ShowRoom( 1 )
ShowRoom( 2 )
ShowRoom( 3 )

Case 4

EntityParent G_player, A_room( 3 )
HideAllRooms
ShowRoom( 2 )
ShowRoom( 3 )
ShowRoom( 4 )

Case 5

EntityParent G_player, A_room( 3 )
HideAllRooms
ShowRoom( 2 )
ShowRoom( 3 )
ShowRoom( 4 )

Case 6

EntityParent G_player, A_room( 4 )
HideAllRooms
ShowRoom( 3 )
ShowRoom( 4 )
ShowRoom( 5 )

Case 7

EntityParent G_player, A_room( 4 )
HideAllRooms
ShowRoom( 3 )
ShowRoom( 4 )
ShowRoom( 5 )
G_message2$ = "Ha! You run away. You are scaredy-cat pussy!"

Case 8

EntityParent G_player, A_room( 5 )
HideAllRooms
ShowRoom( 4 )
ShowRoom( 5 )
G_message2$ = "I enemy! I angry!! You die now!!!"

End Select

EndIf

End Function



Function CreateArena()
; Creates the arena.
; Note that I've used separately named locals for the triggerplates here for example purposes, although it's not efficient.

A_room( 1 ) = CreateRoomType2( 1 ) ; Left side end room.
ScaleMesh A_room( 1 ), 8.0, 3.0, 8.0
Local plate1 = CreateTriggerplate( -1, 8.0, 3.0, A_room( 1 ) )
RotateEntity plate1, 0.0, 270.0, 0.0, True
PositionEntity plate1, 3.95, 1.5, 0.0, True
Local sphere1 = CreateSphere( 8, A_room( 1 ) )
PositionEntity sphere1, -2.0, 1.0, 2.0
NameEntity sphere1, 100 ; Give the sphere an id of 100. The id is a positive value so its visibility will be modified by the visibility management system.
Local sphere2 = CreateSphere( 8, A_room( 1 ) )
PositionEntity sphere2, -2.0, 1.0, -2.0
Local brush = CreateBrush()
BrushAlpha brush, 0.5
PaintMesh sphere2, brush ; Paint the mesh with a brush with an alpha of 0.5. The alpha level setting will persist after the mesh entity has been hidden and shown by the visibility management system as the alpha was set for the mesh data and not the entity data.
FreeBrush brush
NameEntity sphere2, 101 ; Give the sphere an id of 101. The id is a positive value so its visibility will be modified by the visibility management system.
PositionEntity A_room( 1 ), -16.0, 0.0, 0.0, True
NameEntity A_room( 1 ), 1

A_room( 2 ) = CreateRoomType1( 2 )
ScaleMesh A_room( 2 ), 8.0, 3.0, 8.0
Local plate2 = CreateTriggerplate( -2, 8.0, 3.0, A_room( 2 ) )
RotateEntity plate2, 0.0, 90.0, 0.0, True
PositionEntity plate2, -3.95, 1.5, 0.0, True
Local plate3 = CreateTriggerplate( -3, 8.0, 3.0, A_room( 2 ) )
RotateEntity plate3, 0.0, 270.0, 0.0, True
PositionEntity plate3, 3.95, 1.5, 0.0, True
PositionEntity A_room( 2 ), -8.0, 0.0, 0.0, True
NameEntity A_room( 2 ), 2

A_room( 3 ) = CreateRoomType1( 1 ) ; Middle room.
ScaleMesh A_room( 3 ), 8.0, 3.0, 8.0
Local plate4 = CreateTriggerplate( -4, 8.0, 3.0, A_room( 3 ) )
RotateEntity plate4, 0.0, 90.0, 0.0, True
PositionEntity plate4, -3.95, 1.5, 0.0, True
Local plate5 = CreateTriggerplate( -5, 8.0, 3.0, A_room( 3 ) )
RotateEntity plate5, 0.0, 270.0, 0.0, True
PositionEntity plate5, 3.95, 1.5, 0.0, True
NameEntity A_room( 3 ), 3

A_room( 4 ) = CreateRoomType1( 2 )
ScaleMesh A_room( 4 ), 8.0, 3.0, 8.0
Local plate6 = CreateTriggerplate( -6, 8.0, 3.0, A_room( 4 ) )
RotateEntity plate6, 0.0, 90.0, 0.0, True
PositionEntity plate6, -3.95, 1.5, 0.0, True
Local plate7 = CreateTriggerplate( -7, 8.0, 3.0, A_room( 4 ) )
RotateEntity plate7, 0.0, 270.0, 0.0, True
PositionEntity plate7, 3.95, 1.5, 0.0, True
PositionEntity A_room( 4 ), 8.0, 0.0, 0.0, True
NameEntity A_room( 4 ), 4

A_room( 5 ) = CreateRoomType2( 1 ) ; Right side end room.
ScaleMesh A_room( 5 ), 8.0, 3.0, 8.0
Local plate8 = CreateTriggerplate( -8, 8.0, 3.0, A_room( 5 ) )
RotateEntity plate8, 0.0, 270.0, 0.0, True
PositionEntity plate8, 3.95, 1.5, 0.0, True
CreateBadguy( 200, 5, -2.5 )
RotateEntity A_room( 5 ), 0.0, 180.0, 0.0, True
PositionEntity A_room( 5 ), 16.0, 0.0, 0.0, True
NameEntity A_room( 5 ), 5

End Function



Function CreateBadguy( badguy_id, room_id, x# = 0.0, y# = 0.0, z# = 0.0, yaw# = 0.0 )
Local badguy = CreateCube( A_room( room_id ) )
PositionMesh badguy, 0.0, 1.0, 0.0
Local brush = CreateBrush( 200.0, 0.0, 0.0 )
PaintMesh badguy, brush
FreeBrush brush
PositionEntity badguy, x#, y#, z# ; Position the badguy in the local space of the room it is in.
RotateEntity badguy, 0.0, yaw#, 0.0 ; Set the yaw rotation of the badguy in the local space of the room it is in.
NameEntity badguy, badguy_id ; Assign an id to the badguy. The id should be a positive value so its visibility is modified by the visibility management system.
End Function



Function CreateRoomType1( color_set = 1, parent = 0 )

Local room = CreateMesh()

Local brush = CreateBrush ()

If color_set = 1 Then BrushColor brush, 0.0, 200.0, 0.0 : Else BrushColor brush, 0.0, 0.0, 200.0

; -- Create wall 1 (frontface pointed at z+).
Local quad1 = CreateQuad()
PositionMesh quad1, 0.0, 0.5, -0.5
PaintMesh quad1, brush
AddMesh quad1, room
FreeEntity quad1
;^^^^^^

; -- Create wall 2 (frontface pointed at z-).
Local quad2 = CreateQuad()
FlipMesh quad2
PositionMesh quad2, 0.0, 0.5, 0.5
PaintMesh quad2, brush
AddMesh quad2, room
FreeEntity quad2
;^^^^^^

; -- Create floor (frontface pointed at y+).
Local quad3 = CreateQuad()
RotateMesh quad3, 270.0, 0.0, 0.0
If color_set = 1 Then BrushColor brush, 0.0, 0.0, 200.0 : Else BrushColor brush, 0.0, 200.0, 0.0
PaintMesh quad3, brush
AddMesh quad3, room
FreeEntity quad3
;^^^^^^

FreeBrush brush

EntityParent room, parent

EntityType room, C_COLLTYPE_ARENA

Return room
End Function



Function CreateRoomType2( color_set = 1, parent = 0 )

Local room = CreateRoomType1( color_set )

Local brush = CreateBrush ()

If color_set = 1 Then BrushColor brush, 0.0, 200.0, 0.0 : Else BrushColor brush, 0.0, 0.0, 200.0

; -- Create wall 3 (frontface pointed at x+).
Local quad = CreateQuad()
RotateMesh quad, 0.0, 270.0, 0.0
PositionMesh quad, -0.5, 0.5, 0.0
PaintMesh quad, brush
AddMesh quad, room
FreeEntity quad
;^^^^^^

FreeBrush brush

EntityParent room, parent

EntityType room, C_COLLTYPE_ARENA

Return room

End Function



Function HideAllRooms()
Local room_id
For room_id = 1 To C_NUM_ROOMS
HideRoom( room_id )
Next
End Function



Function HideRoom( room_id )
HideRoomContents A_room( room_id )
End Function



Function HideRoomContents( entity )
Local i

If Sgn( Int( EntityName( entity ) ) ) = 1
EntityAlpha entity, 0.0
EndIf

Local num_children = CountChildren( entity )
For i = 1 To num_children
HideRoomContents GetChild( entity, i )
Next
End Function



Function ShowRoom( room_id )
ShowRoomContents( A_room( room_id ) )
End Function



Function ShowRoomContents( entity )
Local i

If Sgn( Int( EntityName( entity ) ) ) = 1
EntityAlpha entity, 1.0
EndIf

Local num_children = CountChildren( entity )
For i = 1 To num_children
ShowRoomContents GetChild( entity, i )
Next
End Function


Code :
Code (blitzbasic) Select
; No code here sorry.

; You'll find the code at: http://www.blitzbasic.com/codearcs/codearcs.php?code=1901


Comments : none...