KICKABLE OR ANCHORED ODE PIECES - PART 2

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

Look some figures of our exercice:




PREVIOUS LESSON NEXT LESSON
T. CONTENTS HOMEPAGE

DISCLAIMER: This material can be translated for any language, and reproduced total or partially by anybody using any type of media. But, please, don't put your name like author. And let me know if it was useful (americo@dmu.com).The use of any code, 3D model or technique published is free and doesn't need to have any reference about this source.The author is not responsible for any damage that the material can cause to your professional or sexual life.