
At this lesson the exercice will be the creation of a "shooter" that will be our usual customizeable "legoic", now having a gun. But, using a similar technique, a "shooter" can be any "false avatar" - a tank, a cannon etc.
The more important "part" of the "ODE piece" that will be the "shooter" is: the bullet. Like we have the "logo" in the ColumnLogo piece , or the "door" in the WallDoor piece, here we have, defined, inside the 3ds max: the bullet, a little sphere inside the barrel of the weapon.
To do our exercice you need to download:
Let's go to explain how the gun works.
And how we code this application?
Easy...
First, to define the Class LegoicMGunODE:
TGroup subclass: #LegoicMGunODE instanceVariableNames: 'inControl tframe body geom pickDelta bullet ' classVariableNames: '' poolDictionaries: 'OpenGLConstants ' category: 'MyCroquet'
New here is the instance variable bullet, defined in:
initialize
super initialize.
tframe := (TLoad3DSMax new initializeWithFileName:
(FileDirectory pathFrom:
{FileDirectory default pathName. 'Content'. 'legoicMGunODE'. 'legoicMGunODE.ase'})
scale: 0.1 shadeAngle: 90.1 textureMode: GLReplace) frame.
tframe collapse.
tframe boundsDepth: 3.
tframe initBounds.
self addChild: tframe.
tframe objectOwner: self.
body:=ODEBody new.
body position: self globalPosition.
body massSphere: 100.
self body:body.
geom:= ODEBox new extent: 6@6@6 .
geom body: self body.
bullet := (tframe find:[:frm | frm objectName = 'bullet']) at: 1.
inControl := false.
Like we will use bullet in the Class Private (Smaltalk doesn't have properties...) we need to create the method:
bullet ^bullet.
The other methods of this Class are the same of the usual "false avatar".
We will use the same Class Private of the previous lesson, but we need to add some new instance variables:
TeapotMorph subclass: #Private
instanceVariableNames: 'tframe cworld sceneObjects floor world space contacts prevTime
rnd wD12x7x1a wD12x7x1abody ahead cL4x6x4a cL4x6x4abody on
cL4x6x4afly lG shot bulletInitT bulletInitO shotS expS recS '
classVariableNames: ''
poolDictionaries: 'OpenGLConstants '
category: 'MyCroquet'
This new variables, in bold, are defined in:
initializeDefaultSpace
| base|
on:=0.
cL4x6x4afly:=0.
shot:=0.
"loading the sound files"
shotS:=StreamingMP3Sound onFileNamed:
(FileDirectory pathFrom:
{FileDirectory default pathName. 'Content'. 'legoicMGunODE' .'gunshot2.mp3'}) .
expS:=StreamingMP3Sound onFileNamed:
(FileDirectory pathFrom:
{FileDirectory default pathName. 'Content'. 'legoicMGunODE' .'explosion2.mp3'}) .
recS:=StreamingMP3Sound onFileNamed:
(FileDirectory pathFrom:
{FileDirectory default pathName. 'Content'. 'legoicMGunODE' .'gunrecharging.mp3'}) .
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.
lG := LegoicMGunODE new.
lG translationX: 0 y: -2 z: 0.
cworld addChild: lG.
world add: (lG body).
space add: (lG geom).
sceneObjects at:lG body id put: {lG. lG body. lG geom}.
bulletInitT:= lG bullet translation.
bulletInitO:= lG bullet orientation.
self addCube. "required"
self transferCam: lG.
^ cworld
CODE COMMENTS
We have something new also, here:
keyStroke: anEvent | kv kc | kc := anEvent keyCharacter asLowercase. kv := anEvent keyValue. kv = 30 ifTrue:[ ahead:=true. lG translation: lG translation + (lG localTransform column3*-0.5). lG body position: lG globalPosition. ]. kv = 31 ifTrue:[ ahead:=false. lG translation: lG translation + (lG localTransform column3*0.5). lG body position:lG globalPosition. ]. kv = 28 ifTrue:[ lG addRotationAroundY:2. lG body quaternion: lG quaternion. ]. kv = 29 ifTrue:[ lG addRotationAroundY:-2. lG body quaternion: lG quaternion. ]. kc = $o ifTrue:[ on:=1. ]. kv = 5 ifTrue:[ "Insert" self addWallDoor12x7x1a. self addColumnLogo4x6x4a. ]. kc = $b ifTrue:[ shot=0 ifTrue:[ shot:=1. bulletInitT:= lG bullet translation. bulletInitO:= lG bullet orientation. shotS play. ]. ]. (#(28 29 30 31) includes: kv) ifTrue: [^self]. kv = 27 ifTrue:[ "Esc " ^self transferCam:lG]. super keyStroke: anEvent.
CODE COMMENTS
The action is really here:
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.
].
(shot=1) ifTrue:[
lG bullet translation: lG bullet translation + (lG bullet localTransform column3*-2). " A good velocity..."
((lG bullet distanceTo: lG)>20) ifTrue:[
lG bullet translation: bulletInitT .
lG bullet orientation: bulletInitO.
recS play.
shot:=0.
].
(cL4x6x4a)ifNotNil:[
((lG bullet distanceTo: cL4x6x4a)<4) ifTrue:[ "What to do in the collision"
cL4x6x4abody linearVel: 5@ 5@ -5.
cL4x6x4abody angularVel: 5@ 0.5@ -5.
expS play.
lG bullet translation: bulletInitT .
lG bullet orientation: bulletInitO.
recS play.
shot:=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:[
lG translationX: 0 y: -2 z: 0.
].
(ahead=false & on=0 ) ifTrue:[
lG translationX: 0 y: -2 z: 0.
].
(ahead=true & on=1) ifTrue:[
lG translation:lG translation + (lG localTransform column3*0.5).
].
(ahead=false & on=1 ) ifTrue:[
lG translation: lG translation - (lG localTransform column3*0.5).
].
]].
].
world quickStep: dur.
contacts empty.
CODE COMMENTS
The other methods of the Class Private are the same of the previous lesson.
I would like to remember, at the end of this important lesson, that Croquet is not a game engine. But it's possible to create some funny interactive sceneries for the Croquet spaces.
PREVIOUS LESSON NEXT LESSON T. CONTENTS HOMEPAGE
