
At this lesson we will include, in our new ODE Croq city, a "kickable piece". It will be the: ColumnLogo4x6x4 we have seeing in previous lesson.
We begin creating a new Class for it:
TGroup subclass: #ColumnLogo4x6x4ODE instanceVariableNames: 'inControl tframe body geom pickDelta logo ' classVariableNames: '' poolDictionaries: 'OpenGLConstants ' category: 'MyCroquet'
You remember that this piece has a rotating part (logo). We need to do this rotation using a the TimeStep loop but, here, the TimeStep loop of the Class Private. So: we define a new instance variable whose name is logo. We will need to access this variable from the Class Private. Smaltalk doesn't have the concept of "property" of an object, like Java or C++. We will need to create a new method:
logo ^logo.that we can call in the Private Class, using an object-reference of the Class ColumnLogo4x6x4ODE.
The other methods of the Class ColumnLogo4x6x4ODE are the usual for an ODE piece: body, body: ,geom, geom:, handlesPointerDown, isComponent, pick, pointerDown, pointerUp, step, handlesKeyboard, keyDown.
In the "initialize" method, we try to find the "logo" part:
initialize
super initialize.
tframe := (TLoad3DSMax new initializeWithFileName:
(FileDirectory pathFrom:
{FileDirectory default pathName. 'Content'. 'columnLogo4x6x4'. 'columnLogo4x6x4.ase'})
scale: 0.1 shadeAngle: 90.1 textureMode: GLReplace) frame.
tframe collapse.
tframe boundsDepth: 3.
tframe initBounds.
self addChild: tframe.
tframe objectOwner: self.
logo := (tframe find:[:frm | frm objectName = 'logo']) at: 1.
inControl := false.
In the Class Private we have many new things.
When we have more that one ODE piece included in the world/space, we will have around 30 seconds of "unstability".The pieces are beeing installed and their "bodies-geoms" collide, one against others. Do you remember how we create the reaction of the car to collisions in the previous lesson? We wrote some lines of code to define that, when we have any collision, the car goes back some units. We can't put this lines of code working until the world/space is "stable" and doesn't have any of the "initial collisions"!
So we will define an standard behavior for the user:
To create this procedure we have the green code in the stepAt method. Look:
stepAt: currentTime
| dur nCollision|
super step.
cL4x6x4a ifNotNil:[
cL4x6x4a logo addRotationAroundY: 1.
(cL4x6x4afly=1)ifTrue:[
cL4x6x4abody linearVel: 5@ 5@ -5.
cL4x6x4abody angularVel: 5@ 0.5@ -5.
]
].
wD12x7x1a ifNotNil:[
wD12x7x1abody position:28.0@0.50@-26.0.
wD12x7x1abody rotationAroundZ:0.
].
space ifNil: [^self ].
world connect.
space connect.
dur := ((currentTime asFloat) - (prevTime asFloat)) / 1000.
prevTime := currentTime.
sceneObjects do:[:id|
| each body |
each := id at:1.
body := id at:2.
each isStepping not ifTrue:[
each localTransform: body transform transposed.
]
ifFalse:[
body linearVel: each globalPosition - (body position) * 10.
body position: each globalPosition.
body quaternion: (each quaternion)
]
].
space collideDo: [:geom1 :geom2 |
nCollision := contacts
addContact: geom1
with: geom2
bounce: 0.1
bounceVel: 0.1
softCFM: 0.1.
nCollision > 0 ifTrue: [
wD12x7x1a ifNotNil:[
self verifycL4x6x4a.
(ahead=true & on=0 ) ifTrue:[
bC translationX: 0 y: -2 z: 0.
].
(ahead=false & on=0 ) ifTrue:[
bC translationX: 0 y: -2 z: 0.
].
(ahead=true & on=1) ifTrue:[
bC translation:bC translation+( bC localTransform column3*0.5).
].
(ahead=false & on=1 ) ifTrue:[
bC translation: bC translation-( bC localTransform column3*0.5).
].
]].
].
world quickStep: dur.
contacts empty.
We have created a flag: on that is setted by the pressing of the "o" key. It's an instance variable, like you can see in the definition of the Private Class:
TeapotMorph subclass: #Private instanceVariableNames: 'tframe cworld sceneObjects floor world space contacts prevTime rnd wD12x7x1a wD12x7x1abody ahead bC cL4x6x4a cL4x6x4abody on cL4x6x4afly ' classVariableNames: '' poolDictionaries: 'OpenGLConstants ' category: 'MyCroquet'
And the keyStroke method defines the pressing of "o" - also in green.
keyStroke: anEvent | kv kc | kc := anEvent keyCharacter asLowercase. kv := anEvent keyValue. kv = 30 ifTrue:[ ahead:=true. bC translation: bC translation+( bC localTransform column3*-0.5). bC body position: bC globalPosition. ]. kv = 31 ifTrue:[ ahead:=false. bC translation: bC translation+( bC localTransform column3*0.5). bC body position:bC globalPosition. ]. kv = 28 ifTrue:[ bC addRotationAroundY:2. bC body quaternion: bC quaternion. ]. kv = 29 ifTrue:[ bC addRotationAroundY:-2. bC body quaternion: bC quaternion. ]. kc = $o ifTrue:[ on:=1. ]. kv = 5 ifTrue:[ "Insert" self addWallDoor12x7x1a. self addColumnLogo4x6x4a. ]. (#(28 29 30 31) includes: kv) ifTrue: [^self]. kv = 27 ifTrue:[ "Esc " ^self transferCam:bC]. super keyStroke: anEvent.
The initial value of "on" is defined in the initializeDefaultSpace method:
initializeDefaultSpace
| base|
on:=0.
cL4x6x4afly:=0.
cworld := TSpace new.
self makeLight: cworld.
base :=BasePlateA70x3x70 new.
base translationX: 0 y: -6.3 z: 0.
cworld addChild: base.
prevTime := Time millisecondClockValue.
world := ODEWorld new.
space := ODESpace new.
world add: (contacts := ODEContactGroup new).
sceneObjects := Dictionary new.
rnd := Random new.
bC := BaseCar10x2x8ODE new.
bC translationX: 0 y: -2 z: 0.
bC addRotationAroundY:180.
cworld addChild: bC.
world add: ( bC body).
space add: (bC geom).
sceneObjects at: bC body id put: {bC. bC body. bC geom}.
self addCube. "required"
self transferCam: bC.
^ cworld
For the exercice of this lesson, the Private Class has some methods we have seeing in previous lessons: addWallDoor12x7x1a, transferCam, wantsSteps, addCube.
We need to create a new method to add the ColumnLogo:
addColumnLogo4x6x4a
| geom |
cL4x6x4a := ColumnLogo4x6x4ODE new.
cworld addChild: cL4x6x4a.
world add: (cL4x6x4abody:= ODEBody new).
cL4x6x4abody position:16@-2.5@ -25.
cL4x6x4abody rotationAroundZ:-90.
cL4x6x4abody massSphere: 90.
space add: (geom := ODEBox new extent: 4@6@4).
geom body: cL4x6x4abody .
sceneObjects at: cL4x6x4abody id put: {cL4x6x4a. cL4x6x4abody . geom}
CODE COMMENTS
These instance variables will be used in many places but there are one that is VERY IMPORTANT. Sometimes we need to capture when the car collides against a defined piece.By example, to make an specific sound or an specific movement. At this exercice, like we don't have gravity in our world/space, we need to define the movement that the ColumnLogo will have after the collision. How to do this?
You need to know first the concept of "distance" of 2 pieces. Better to look to the figure:
To capture the collision Car versus ColumLogo we create a method using the distanceTo method of the TFrame Class :
verifycL4x6x4a | onetime dist | onetime:=0. dist:=(bC distanceTo:cL4x6x4a). (dist<7 & onetime=0) ifTrue: [ "Transcript show: ( 'cL4x6x4a'). - Used for test" cL4x6x4afly := 1. onetime:=1. ].
CODE COMMENTS
cL4x6x4a ifNotNil:[
cL4x6x4a logo addRotationAroundY: 1.
(cL4x6x4afly=1)ifTrue:[
cL4x6x4abody linearVel: 5@ 5@ -5.
cL4x6x4abody angularVel: 5@ 0.5@ -5.
]
].
The ColumnLogo will have a funny movement up when it receives a collision (remember that we don't have gravity).
Note that here we are also rotating the logo.
Look some figures of our exercice:
PREVIOUS LESSON NEXT LESSON T. CONTENTS HOMEPAGE
