Today I had a chance to play with Gideros SDK. In order to get a better understanding about the SDK and Lua in general, I decided to port some AS3 code. Here are some code from Keith Peter’s book, Advanced AS3 animations, in Lua.
I haven’t fully understood Lua so these code might look bad in the eyes of Lua experts 🙂 but at least it works.
[note color=”#FFCC00″]You can get the original, fully commented AS3 code from http://www.apress.com/9781430216087[/note]
First, the Vector2D. I don’t know if Lua has a built-in Vector class, maybe it does but as part of the learning process, I wrote one myself. Porting Vector2D.as
to Vector2D.lua
was almost as easy as copy-pasting.
--[[ Vector 2D class. Ported to Lua from Keith Peter's AS3 Vector2D Author : Anggie Bratadinata | www.masputih.com --]] Vector2D = {} Vector2D.__index = Vector2D -- constructor function Vector2D.new(initX,initY) local instance = { x = initX, y = initY } setmetatable(instance,Vector2D) return instance end --[[ BASIC MATH OPS --]] function Vector2D:dotProduct(v2) return self.x * v2.x + self.y * v2.y end function Vector2D:angleFrom(v2) local v1 = self if self:isNormalized() == true then v1 = self:clone() v1:normalize() end if v2:isNormalized() == true then v2 = v2:clone() v2:normalize() end return math.acos(v1:dotProd(v2)) end function Vector2D:add(v2) self.x = self.x + v2.x self.y = self.y + v2.y end function Vector2D:subtract(v2) self.x = self.x - v2.x self.y = self.y - v2.y end function Vector2D:multiply(value) self.x = self.x * value self.y = self.y * value end function Vector2D:divide(value) self.x = self.x / value self.y = self.y / value end --[[ LENGTH --]] function Vector2D:setLength(value) local a = self:getAngle() self.x = math.cos(a) * value self.y = math.sin(a) * value end function Vector2D:getLength() return math.sqrt(self:getSquareLength()) end function Vector2D:getSquareLength() return self.x * self.x + self.y * self.y end -- normalization function Vector2D:normalize() if self:getLength() == 0 then self.x = 1 self.y = 0 else local l = self:getLength() self.x = self.x/l self.y = self.y/l end return self end function Vector2D:isNormalized() return self:getLength() == 1 end --[[ ANGLE --]] function Vector2D:getAngle() return math.atan2(self.y,self.x) end function Vector2D:setAngle(value) local l = self:getLength() self.x = math.cos(value) * l self.y = math.sin(value) * l end --[[ DISTANCE --]] -- get the distance of the given vector from this vector function Vector2D:getDistanceOf(v2) return math.sqrt(self:getSquareDistance(v2)) end function Vector2D:getSquareDistance(v2) local dx = v2.x - self.x local dy = v2.y - self.y return dx * dx + dy * dy end --[[ UTILITIES --]] -- determine if the given vector is to the right (+1) or to the left (-1) -- of this vector function Vector2D:getSignOf(v2) local perp = self:getPerpendicular() if perp:dotProduct(v2) < 0 then return -1 else return 1 end end -- return a new vector that is perpendicular to this vector function Vector2D:getPerpendicular() return Vector2D.new(-self.y,self.x) end function Vector2D:truncate(maxLength) self:setLength(math.min(maxLength,self:getLength())) end function Vector2D:zero() self.x = 0 self.y = 0 end function Vector2D:isZero() if self.x == 0 and self.y == 0 then return true else return false end end function Vector2D:equals(v2) return self.x == v2.x and self.y == v2.y end function Vector2D:clone() return Vector2D.new(self.x,self.y) end function Vector2D:toString() print("Vector2D[",self.x,",",self.y,"]"); end -- so that this class can be imported by "geom.Vector2D" return Vector2D
Below is the Vehicle class which is the base class of all vehicles. Vehicles can be moved by updating their velocity vector.
require "geom.Vector2D" -- subclass Gideros' Shape class Vehicle = Core.class(Shape) function Vehicle:init() --instance vars self.positionVector = Vector2D.new(0,0) self.mass = 1 self.velocityVector = Vector2D.new(0,0) self.maxSpeed = 4 self:draw(0x000000) end function Vehicle:draw(color) self:setFillStyle(Shape.SOLID,color) self:beginPath() self:moveTo(0,0) self:lineTo(10,5) self:lineTo(0,10) self:lineTo(0,0) self:endPath() end function Vehicle:update() local vel = self.velocityVector vel:truncate(self.maxSpeed) local pos = self.positionVector pos:add(vel) local w = application:getContentWidth() local h = application:getContentHeight() if pos.x > w then pos.x = w vel.x = vel.x * -1 elseif pos.x < 0 then pos.x = 0 vel.x = vel.x * -1 end if pos.y > h then pos.y = h vel.y = vel.y * -1 elseif pos.y < 0 then pos.y = 0 vel.y = vel.y * -1 end self:setPosition(pos.x,pos.y) self:setRotation(vel:getAngle() * 180/math.pi) end function Vehicle:setPosition(x,y) -- call super class method Shape.setPosition(self,x,y) -- update position vector self.positionVector.x = x; self.positionVector.y = y; end return Vehicle
Here’s the SteeredVehicle class that I can steer by adding steering force (vector). This class has seek()
and flee()
methods, both take Vector2D as their parameters, which are used for setting the object’s behavior. If seek
is called, the object will try to get to the designated vector. If flee
is called, it will try to get away from the designated vector.
require "geom.Vector2D" require "vehicles.Vehicle" -- subclass the Vehicle class SteeredVehicle = Core.class(Vehicle) -- constructor function SteeredVehicle:init() Vehicle.init(self) self.steeringForce = Vector2D.new(1,2) self.maxForce = 1 self.mass = 10 end function SteeredVehicle:update() self.steeringForce:truncate(self.maxForce) self.steeringForce:divide(self.mass) self.velocityVector:add(self.steeringForce) self.steeringForce = Vector2D.new(0,0) Vehicle.update(self) end function SteeredVehicle:seek(target) local desiredVelocity = target:clone() desiredVelocity:subtract(self.positionVector) desiredVelocity:normalize() desiredVelocity:multiply(self.maxSpeed) local force = desiredVelocity:clone() force:subtract(self.velocityVector) self.steeringForce:add(force) end function SteeredVehicle:flee(target) local desiredVelocity = target:clone() desiredVelocity:subtract(self.positionVector) desiredVelocity:normalize() desiredVelocity:multiply(self.maxSpeed) local force = desiredVelocity:clone() force:subtract(self.velocityVector) self.steeringForce:subtract(force) end
Finally, the main application file.
require "vehicles.SteeredVehicle" local v = SteeredVehicle.new() v:draw(0x00FF00) v:setPosition(100,100) v.velocityVector:setLength(5) v.velocityVector:setAngle(-math.pi/4) local v1 = SteeredVehicle.new() v1:draw(0x0000FF) v1:setPosition(300,300) v1.velocityVector:setLength(5) v1.velocityVector:setAngle(-math.pi/2) local v2 = SteeredVehicle.new() v2:draw(0xFF0000) v2:setPosition(0,0) v2.velocityVector:setLength(4) v2.velocityVector:setAngle(math.pi/3) -- fps local fpsLabel = TextField.new(nil,"fps") fpsLabel:setTextColor(0x000000) fpsLabel:setPosition(10,10) function onEnterFrame(event) v:seek(v2.positionVector) v:flee(v1.positionVector) v1:seek(v.positionVector) v1:flee(v2.positionVector) v2:seek(v1.positionVector) v2:flee(v.positionVector) v:update() v1:update() v2:update() fpsLabel:setText("fps ".. application:getFps()) end stage:addChild(v) stage:addChild(v1) stage:addChild(v2) stage:addChild(fpsLabel) stage:addEventListener(Event.ENTER_FRAME,onEnterFrame)[box title=”The source” color=”#fede9f”][download id=”19″ format=”2″][/box]
Here’s is the animation running on Gideros player ( it actually runs smoothly, it looks stuttering only on the screencap).
kk postingan’a indo-in aja …
ga paham nih