Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Animated Voxel Copter

edited January 2 in Codea Craft Posts: 1,629

The frames were created in the Dual-Joystick Voxel Editor.

Project attached.

The up and down bounce is animated using 8 separate voxel model “frames” created in the Joystick Voxel Editor using the nudge feature. It would probably be simpler to use tweening, but this has a blocky charm to it I think.

Tagged:

Comments

  • Posts: 1,629

    updated

    • VoxelCopter is now a stand-alone class that can be used as a dependency, or just copy-pasted, if anyone wants to use the cute little bugger in their own projects.
    • In addition to the frame-based up and down animation, there is now a tween-based animation of slight turning to the left and then to the right in a smooth loop.
  • edited December 2021 Posts: 1,629

    updated

    • all frames now have a single parent entity so the whole copter can be moved at once
    • the code’s just better. It’s just… better.
  • Posts: 1,303

    Very nice @UberGoober, you're doing some great things!

  • Posts: 1,629

    Thanks Ron!

  • Posts: 190

    It’s very creative. It looks really good!

  • edited January 2 Posts: 1,629

    Updated to include @Bri_G ’s excellent background.

    Thanks for the kind words @binaryblues!

  • Posts: 327
    Also available using WebRepo now!
  • Posts: 190

    @UberGoober I liked the code so much that I made a few changes to your code. I hope you don’t mind. I rewrote the animation in a different way. I divided the model into two parts, the propeller and the fuselage, and then let the propeller rotation around the axis, in this way to achieve the helicopter flight animation.

    https://youtu.be/3ak4peIaERQ

    The code is below:

    Copter = class()
    
    function Copter:init(pos)
        self.camera = scene.camera:get(craft.camera)
        self.state = true
        self.root = scene:entity()
        self.root.position = pos or vec3(0,0,0)
    
        self.topRoot = scene:entity()
        self.topRoot.parent = self.root  
        self.topRoot.position = vec3(0,0,0)
    
        self.top = scene:entity()
        self.top.parent = self.topRoot
        self.top.position = vec3(-8.5,-8.5,-12.5)
        self.vm1 = self.top:add(craft.volume, 1,1,1)
        self.vm1:load(asset.Propellers)
        self.top.active = true
    
        self.body = scene:entity()
        self.body.parent = self.root
        self.body.position = vec3(-8.5,-8.5,-12.5)
        self.vm2 = self.body:add(craft.volume, 1,1,1)
        self.vm2:load(asset.Airframe)
        self.body.active = true
    
        self.angle = 0
        self.zoom = 1
        self.y = 0
    
        touches.addHandler(self, -1, false)
    end
    
    function Copter:update(dt)
        self.topRoot.rotation = quat.eulerAngles(0, self.angle*100,0)
        self.root.rotation = quat.eulerAngles(0,self.angle,0)
        self.root.scale = vec3(self.zoom, self.zoom, self.zoom)
        self.root.y = self.y
        -- print(self.root.worldPosition)
        local a = self.topRoot.worldPosition + vec3(0,0,-1)
        local b = self.topRoot.worldPosition + vec3(0,0,1)
        local c = self.topRoot.worldPosition + vec3(0,-1,0)
        local d = self.topRoot.worldPosition + vec3(0,1,0)    
        scene.debug:line(a, b,color(16, 239, 5))    
        scene.debug:line(c, d,color(239, 19, 5))    
    end
    
    function Copter:interact()
        if not self.open then
            self.open = true
            tween(2.5, self, {angle = 61.8, zoom=1/2}, {easing=tween.easing.backOut,loop = tween.loop.once})
        else
            self.open = false
            tween(2.5, self, {angle = 0, zoom=1}, {easing=tween.easing.backOut,loop = tween.loop.once})
        end
        local t1 = tween(0.5,self,{y=2})
        local t2 = tween(0.5,self,{y=-1})
        local t3 = tween(0.5,self,{y=1})
        local t4 = tween(0.5,self,{y=-2})
        local t5 = tween(0.5,self,{y=3})
        tween.sequence(t1,t2,t3,t4,t5)
    end
    
    
    function Copter:touched(touch)
        if touch.state == BEGAN then
            self:interact()                
    
            -- Returning true will capture this touch and prevent other handlers from getting it
            local origin, dir = self.camera:screenToRay(vec2(touch.x, touch.y))
            -- print(self.root.worldPosition)
            -- Do a raycast to check if touch is hitting the bulb
            local hit = scene.physics:raycast(origin, dir, 300)
            -- print(self.top.position)
            if hit and hit.entity == self.body then
    
                -- Turn up light intensity
                -- self.light.intensity = 3
                -- self.entity.material.diffuse = self.color
                self.state = not self.state
                print("Touch Began (Captured - "..touch.id..")",origin)
                return true
            end
        elseif touch.state == ENDED and self.state then
    
            -- Turn down intensity when touch ends
            -- self.light.intensity = 0.2
            -- self.entity.material.diffuse = self.color * 0.2
            print("Touch Ended (Captured - "..touch.id..")")
        end
    end
    
    function setup()
        -- Create a new craft scene
        scene = craft.scene()
        scene.ambientColor = color(218, 158, 79)
        scene.sky.active = false
    
        -- Setup camera and lighting
        scene.sun.rotation = quat.eulerAngles(125, 125, 0)
    
        -- Helper class for interactive camera  
        myViewer = scene.camera:add(OrbitViewer, vec3( 0,  0,  0), 80, 1, 400)
        myViewer.rx = 0
        myViewer.ry = 0 
    
        myCopter1 = Copter(vec3(0,0,0))   
        myCopter2 = Copter(vec3(-30,0,60))   
    end
    
    function update(dt)
        scene:update(dt)
        myCopter1:update()
        myCopter2:update()
    end
    
    -- Called automatically by codea 
    function draw()
        update(DeltaTime)
        scene:draw()    
        sprite(asset.builtin.UI.Blue_Circle,CurrentTouch.x,CurrentTouch.y)
    end
    
  • Posts: 1,629
    @binaryblues that is seriously cool! I don’t mind at all, in fact, please, anybody who wants to do anything at all with this little bugger, have at it!

    binaryblues: I assume it’s reciprocally fine if I use your version in the Voxel Editor (and elsewhere if needed)?

    I’m not super sure what all the controls do, though, and I’m confused why the copter doesn’t stay in motion—it seems to reverse itself halfway through animating and then come to a stop.
  • Posts: 190

    @UberGoober Glad to hear that. The reason the helicopter is moving like this, is because I’ve added some settings, like let the fuselage rotate and so on, you can start it up by clicking on the screen, if you want to keep it moving, you can change it like this:

    function Copter:update(dt)
        self.topRoot.rotation = quat.eulerAngles(0, self.angle*110.31, 0)
        -- self.root.rotation = quat.eulerAngles(0,self.angle,0)
    end
    
    function Copter:touched(touch)
        if touch.state == BEGAN then
            -- self:interact()                
            tween(0.5, self, {angle = 90.3, zoom=1}, {easing=tween.easing.backInOut,loop = tween.loop.forever})
            -- Returning true will capture this touch and prevent other handlers from getting it
    
    
  • Posts: 1,629
    @binaryblues What tool did you use to separate the voxel model into two pieces?

    And how do I recapture that bounce? It had a nice bounce in your original version, but once I did the code changes you suggested, it just stays in one place as the propeller spins.
  • Posts: 190

    @UberGoober Using CODEA’s built-in VoxelEditor:
    1. load your full helicopter model, then remove the propeller section, save only the fuselage, save as the fuselage,;
    2. load your full helicopter model again, and this time delete the fuselage, just keep the propeller, and then save it as a propeller, so you have two models.
    And you can just rotate the propeller in the code. The slightly tricky part here is determining the offset coordinates of the center of the helicopter model in local space, which I counted manually.
    There was some animation of the y coordinate in the original code, and I commented it out, so you can turn it back on, and here it is:

    function Copter:interact()
        if not self.open then
            self.open = true
            tween(2.5, self, {angle = 61.8, y=0,zoom=1}, {easing=tween.easing.backInOut,loop = tween.loop.once})
        else
            self.open = false
            tween(2.5, self, {angle = 0,y=0, zoom=1}, {easing=tween.easing.backOut,loop = tween.loop.once})
        end
    
        —-[[
        local t1 = tween(0.5,self,{y=2})
        local t2 = tween(0.5,self,{y=-1})
        local t3 = tween(0.5,self,{y=1})
        local t4 = tween(0.5,self,{y=-2})
        local t5 = tween(0.5,self,{y=3})
        tween.sequence(t1,t2,t3,t4,t5)
        --]]
    end
    
    
Sign In or Register to comment.