1.3.1 Update - Move files to new locations, parameter count increased to 5.
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
; BlitzPointer - Adding Pointers to Blitz.
|
||||
; Copyright (C) 2015 Project Kube (Michael Fabian Dirks)
|
||||
;
|
||||
; This program is free software: you can redistribute it and/or modify
|
||||
; it under the terms of the GNU Lesser General Public License as
|
||||
; published by the Free Software Foundation, either version 3 of the
|
||||
; License, or (at your option) any later version.
|
||||
;
|
||||
; This program is distributed in the hope that it will be useful,
|
||||
; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
; GNU General Public License for more details.
|
||||
;
|
||||
; You should have received a copy of the GNU Lesser General Public License
|
||||
; along with this program. If not, see <http:;www.gnu.org/licenses/>.
|
||||
|
||||
; ---------------------------------------------------------------------------- ;
|
||||
; Example 6 - Callbacks
|
||||
; ---------------------------------------------------------------------------- ;
|
||||
; License: Creative Commons Attribution 2.0
|
||||
; Author: Michael Fabian Dirks<michael.dirks@realitybends.de>
|
||||
; Prerequisite: Example 5
|
||||
|
||||
; In my opinion, Callbacks are one of the best things that people have figured
|
||||
; out is useful. We can completely combine all our update logic into one single
|
||||
; function call without ever having to worry about how to handle a new object.
|
||||
; Well, unless you are a terrible planner anyways.
|
||||
|
||||
Include "Example_Shared.bb"
|
||||
ExampleInit()
|
||||
SeedRnd MilliSecs()
|
||||
|
||||
; We'll begin by creating a basic Type that holds our generic object. By itself
|
||||
; this generic object does nothing, but we define custom callbacks later on.
|
||||
; For optimization reasons (saves some memory) we can use a manually defined
|
||||
; index for our callbacks.
|
||||
|
||||
; Using an Index to store our Callbacks is more efficient that storing it for
|
||||
; each entry. By doing this we can also have multiple functions that use the
|
||||
; same structure (Update, PhysicsUpdate, Render, ...). It is wise to reserve
|
||||
; the first index to check for incorrectly created objects.
|
||||
Dim CallbackIndex(10)
|
||||
|
||||
; Here we define our Type. Since each object has an Index, we
|
||||
Type TGeneric
|
||||
; For optimization reasons and saving memory, we just store a callback index.
|
||||
Field Index
|
||||
|
||||
; We only need to store the pointer to our Type object, nothing else.
|
||||
Field Pointer
|
||||
End Type
|
||||
|
||||
Function TGenericUpdate()
|
||||
Local This.TGeneric
|
||||
For This = Each TGeneric
|
||||
If This\Pointer <> 0 Then
|
||||
If BP_CallFunctionII(CallbackIndex(This\Index), This\Pointer) Then
|
||||
Delete This
|
||||
EndIf
|
||||
EndIf
|
||||
Next
|
||||
End Function
|
||||
|
||||
; TGeneric will call a function pointer with the following footprint:
|
||||
; int FUNCTION(pointer)
|
||||
; We will need to implement this function footprint in every callback.
|
||||
|
||||
; Our first Type will hold Cubes that moves up and down.
|
||||
Type TCube
|
||||
; Our TCube is represented by an actual Cube in the world, so let's store it.
|
||||
Field Entity
|
||||
; This is the original position of the Cube. Y is dynamically modified in code.
|
||||
Field PosX#, PosY#, PosZ#
|
||||
; And finally, the time it was created.
|
||||
Field Time
|
||||
End Type
|
||||
; Assign an index to our new Type (starting at 1)
|
||||
Const CALLBACK_INDEX_TCUBE% = 1
|
||||
|
||||
Function TCubeCreate.TCube(X#, Y#, Z#)
|
||||
; Create our Object
|
||||
Local This.TCube = New TCube
|
||||
This\Entity = CreateCube(Example_SceneRoot)
|
||||
This\PosX = X
|
||||
This\PosY = Y
|
||||
This\PosZ = Z
|
||||
This\Time = MilliSecs()
|
||||
|
||||
; And now store it withing TGeneric
|
||||
Local TG.TGeneric = New TGeneric
|
||||
TG\Index = CALLBACK_INDEX_TCUBE
|
||||
; To get the pointer we need (and can store), we just use Int() on the object.
|
||||
TG\Pointer = Int(This)
|
||||
|
||||
; That's all we had to do.
|
||||
Return This
|
||||
End Function
|
||||
|
||||
Function TCubeCallback%(This.TCube)
|
||||
If CallbackIndex(CALLBACK_INDEX_TCUBE) = 0 Then
|
||||
CallbackIndex(CALLBACK_INDEX_TCUBE) = BP_GetFunctionPointer()
|
||||
Return
|
||||
EndIf
|
||||
; Safeguard against stupidity (it affect everyone).
|
||||
If This = Null Then Return True
|
||||
|
||||
Local TimeDiff = (MilliSecs() - This\Time)
|
||||
|
||||
Local YOffset# = Cos((Cos(This\PosX) * 180) + (Sin(This\PosZ) * 180) + (TimeDiff / 10.0)) * 5 + 5
|
||||
PositionEntity This\Entity, This\PosX, This\PosY + YOffset, This\PosZ
|
||||
EntityAlpha This\Entity, 1.0 - (TimeDiff / 5000.0)
|
||||
|
||||
; We will allow the object to exist for at most 5 seconds.
|
||||
If TimeDiff > 5000 Then
|
||||
TCubeDestroy(This)
|
||||
Return True
|
||||
EndIf
|
||||
Return False
|
||||
End Function
|
||||
|
||||
Function TCubeDestroy(This.TCube)
|
||||
; Safeguard against stupidity (it affect everyone).
|
||||
If This = Null Then Return
|
||||
|
||||
FreeEntity This\Entity
|
||||
Delete This
|
||||
End Function
|
||||
|
||||
; And call the function once to automagically register it as a callback.
|
||||
TCubeCallback(Null)
|
||||
|
||||
; Next up is our second Type. It will hold a simple sphere that grows over time.
|
||||
Type TSphere
|
||||
; Again, represented by an actual Sphere in the world.
|
||||
Field Entity
|
||||
; And the time it was created.
|
||||
Field Time
|
||||
End Type
|
||||
; And we will assign the second index to it.
|
||||
Const CALLBACK_INDEX_TSPHERE% = 2
|
||||
|
||||
Function TSphereCreate.TSphere(X#, Y#, Z#)
|
||||
; Initialize our TSphere instance.
|
||||
Local This.TSphere = New TSphere
|
||||
This\Entity = CreateSphere(32, Example_SceneRoot)
|
||||
PositionEntity This\Entity, X, Y, Z
|
||||
This\Time = MilliSecs()
|
||||
|
||||
; And now store it withing TGeneric
|
||||
Local TG.TGeneric = New TGeneric
|
||||
TG\Index = CALLBACK_INDEX_TSPHERE
|
||||
TG\Pointer = Int(This)
|
||||
|
||||
Return This
|
||||
End Function
|
||||
|
||||
Function TSphereUpdate%(This.TSphere)
|
||||
If CallbackIndex(CALLBACK_INDEX_TSPHERE) = 0 Then
|
||||
CallbackIndex(CALLBACK_INDEX_TSPHERE) = BP_GetFunctionPointer()
|
||||
Return
|
||||
EndIf
|
||||
; Safeguard against stupidity (it affect everyone).
|
||||
If This = Null Then Return True
|
||||
|
||||
Local TimeDiff = (MilliSecs() - This\Time)
|
||||
Local TimePrc# = TimeDiff / 2500.0
|
||||
|
||||
Local Stp#, StpSize#
|
||||
Local R#, G#, B#, A#, Scale#
|
||||
;Step
|
||||
StpSize# = 0.05:Stp# = 1.0 / (1.0 - StpSize)
|
||||
R = Interp(1, 1, TimePrc * Stp)
|
||||
G = Interp(1, 1, TimePrc * Stp)
|
||||
B = Interp(1, 0, TimePrc * Stp)
|
||||
;Step
|
||||
StpSize# = 0.20:Stp# = 1.0 / (1.0 - StpSize)
|
||||
R = Interp(R, 1, (TimePrc - 0.05) * Stp)
|
||||
G = Interp(G, 0.75, (TimePrc - 0.05) * Stp)
|
||||
B = Interp(B, 0, (TimePrc - 0.05) * Stp)
|
||||
;Step
|
||||
StpSize# = 0.25:Stp# = 1.0 / (1.0 - StpSize)
|
||||
R = Interp(R, 1, (TimePrc - 0.25) * Stp)
|
||||
G = Interp(G, 0, (TimePrc - 0.25) * Stp)
|
||||
B = Interp(B, 0, (TimePrc - 0.25) * Stp)
|
||||
;Step
|
||||
StpSize# = 0.50:Stp# = 1.0 / (1.0 - StpSize)
|
||||
R = Interp(R, 0, (TimePrc - 0.50) * Stp)
|
||||
G = Interp(G, 0, (TimePrc - 0.50) * Stp)
|
||||
B = Interp(B, 0, (TimePrc - 0.50) * Stp)
|
||||
|
||||
;AlphaStop 1 at (0.8 of 1.0)
|
||||
A = Interp(1, 0, (TimePrc - 0.8) * 5)
|
||||
|
||||
Scale = Interp(1, 20, Sin(TimePrc * 90))
|
||||
ScaleEntity This\Entity, Scale,Scale,Scale
|
||||
EntityColor This\Entity, R * 255, G * 255, B * 255
|
||||
EntityAlpha This\Entity, A
|
||||
|
||||
; We will allow the object to exist for at most 2.5 seconds.
|
||||
If TimeDiff > 2500 Then
|
||||
TSphereDestroy(This)
|
||||
Return True
|
||||
EndIf
|
||||
Return False
|
||||
End Function
|
||||
TSphereUpdate(Null)
|
||||
|
||||
Function TSphereDestroy(This.TSphere)
|
||||
; Safeguard against stupidity (it affect everyone).
|
||||
If This = Null Then Return
|
||||
|
||||
FreeEntity This\Entity
|
||||
Delete This
|
||||
End Function
|
||||
|
||||
; And finally, a terrain that we can pick a spawning position from.
|
||||
Local Terrain = CreateTerrain(256, Example_SceneRoot)
|
||||
ScaleEntity Terrain, 1 / 256.0, 64, 1 / 256.0
|
||||
MoveEntity Terrain, -100, 0, -100
|
||||
ScaleEntity Terrain, 1 / 256.0 * 201, 64, 1 / 256.0 * 201
|
||||
EntityColor Terrain, 25, 92, 25
|
||||
TerrainShading Terrain, True
|
||||
TerrainDetail Terrain, 32767, False
|
||||
EntityPickMode Terrain, 2, True
|
||||
|
||||
RandomTerrain(Terrain, 256, 8, 8)
|
||||
|
||||
MoveEntity Example_Camera, 0, 0, -125
|
||||
;HideEntity Example_Floor
|
||||
|
||||
While Not KeyHit(1)
|
||||
TGenericUpdate()
|
||||
|
||||
; Space to randomize Terrain
|
||||
If KeyHit(57) Then
|
||||
RandomTerrain(Terrain, 256, Rand(1,8), Rand(1,8))
|
||||
EndIf
|
||||
|
||||
Local ZDelta = MouseZSpeed()
|
||||
If KeyHit(2) Or ZDelta > 0 Then
|
||||
CameraPick(Example_Camera, MouseX(), MouseY())
|
||||
TCubeCreate(PickedX(), PickedY(), PickedZ())
|
||||
EndIf
|
||||
|
||||
If KeyHit(3) Or ZDelta < 0 Then
|
||||
CameraPick(Example_Camera, MouseX(), MouseY())
|
||||
TSphereCreate(PickedX(), PickedY(), PickedZ())
|
||||
EndIf
|
||||
|
||||
ExampleUpdate()
|
||||
ExampleLoop()
|
||||
UpdateWorld()
|
||||
Wend
|
||||
|
||||
Function RandomTerrain(Terrain, GridSize, CellsX = 1, CellsZ = 1)
|
||||
Local CellW, CellH, CellX, CellZ, TerX, TerZ, CellPrcX#, CellPrcZ#
|
||||
Local HTL#, HTR#, HBL#, HBR#, HCC#
|
||||
|
||||
CellW = Floor(GridSize / Float(CellsX))
|
||||
CellH = Floor(GridSize / Float(CellsZ))
|
||||
|
||||
Local RowSize = CellsZ + 1
|
||||
Local TempBankSize = (CellsX + 1) * RowSize * 4
|
||||
Local TempBank = CreateBank(TempBankSize)
|
||||
For CellX = 0 To CellsX
|
||||
For CellZ = 0 To CellsZ
|
||||
PokeFloat TempBank, 4 * (CellX * RowSize + CellZ), Rnd(0, 1)
|
||||
Next
|
||||
Next
|
||||
|
||||
Local OCellX, OCellZ:OCellX = -1: OCellZ = -1
|
||||
For TerX = 1 To (GridSize - 1)
|
||||
CellX = Floor(TerX / Float(CellW))
|
||||
CellPrcX = (TerX Mod CellW) / Float(CellW - 1)
|
||||
For TerZ = 1 To (GridSize - 1)
|
||||
CellZ = Floor(TerZ / Float(CellH))
|
||||
CellPrcZ = (TerZ Mod CellH) / Float(CellH - 1)
|
||||
;DebugLog CellPrcZ
|
||||
|
||||
If CellX <> OCellX Or CellZ <> OCellZ Then
|
||||
HTL = PeekFloat(TempBank, 4 * (((CellX + 0) * RowSize + (CellZ + 0))))
|
||||
HTR = PeekFloat(TempBank, 4 * (((CellX + 1) * RowSize + (CellZ + 0))))
|
||||
HBL = PeekFloat(TempBank, 4 * (((CellX + 0) * RowSize + (CellZ + 1))))
|
||||
HBR = PeekFloat(TempBank, 4 * (((CellX + 1) * RowSize + (CellZ + 1))))
|
||||
OCellX = CellX:OCellZ = CellZ
|
||||
EndIf
|
||||
|
||||
Local PrcX#, PrcZ#
|
||||
; Normally, you'd use a bezier curve for this.
|
||||
PrcX = (Sin(-90 + CellPrcX*180) * 0.5 + 0.5)
|
||||
PrcZ = (Sin(-90 + CellPrcZ*180) * 0.5 + 0.5)
|
||||
Local Height# = Interp(Interp(HTL, HTR, PrcX), Interp(HBL, HBR, PrcX), PrcZ)
|
||||
|
||||
ModifyTerrain Terrain, TerX, TerZ, Height
|
||||
Next
|
||||
Next
|
||||
FreeBank TempBank
|
||||
End Function
|
||||
|
||||
Function Min#(A#, B#)
|
||||
If B < A Then Return B
|
||||
Return A
|
||||
End Function
|
||||
|
||||
Function Max#(A#, B#)
|
||||
If B > A Then Return B
|
||||
Return A
|
||||
End Function
|
||||
|
||||
Function Interp#(L#, R#, Lerp#)
|
||||
Lerp = Min(Max(Lerp, 0.0), 1.0)
|
||||
Return L * (1.0 - Lerp) + R * Lerp
|
||||
End Function
|
||||
;~IDEal Editor Parameters:
|
||||
;~C#Blitz3D
|
||||
Reference in New Issue
Block a user