Howdy, Stranger!

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

A Fish for your games

edited July 2013 in Code Sharing Posts: 437

This can be used as a toy or part of a game (like I'm doing).
The behaviour of the animal is to swim around the screen, it has some adaptative algorithms to shape the parts of the body with the swimming movement. You can touch the screen and the fish can follow your finger XD

It is ready to add the online multiplayer layer to the code so you can have an online fishbowl.

To use it just create a fish = Fish(location,maxSpeed,maxForce), and then use fish:draw()

Boid = class()
 
-- constructor used by fish
function Boid:init(  _location, _maxSpeed,  _maxForce)
    self.velocity = vec2( math.random( -maxSpeed, maxSpeed ), math.random( -maxSpeed, maxSpeed ) )
    self:createBoid(_location, _maxSpeed, _maxForce)
end
 
-- constructor used by bubbles
--[[
function Boid:init(  _location,  _maxSpeed,  _maxForce, _velocity)
    self.velocity = _velocity
    self:createBoid(_location, _maxSpeed, _maxForce)
end
]]--
function Boid:createBoid( _location, _maxSpeed, _maxForce) 
    self.location         = _location
    self.maxSpeed         = _maxSpeed
    self.maxForce         = _maxForce
    self.acceleration     = vec2( 0, 0 )
    self.wanderTheta       = 0
    self.hasArrive         = false
end
 
function Boid:update()
    if not self.velocity then return end
    self.velocity = self.velocity + self.acceleration
    self.velocity = vec2( 
        math.min( self.velocity.x, self.maxSpeed),
        math.min( self.velocity.y, self.maxSpeed)
    )
    self.location = self.location + self.velocity
    self.acceleration = self.acceleration * 0
end
 
function Boid:debugRender()
    noStroke()
    fill(255, 0, 0,255)
    ellipse(self.location.x, self.location.y, 10, 10);
end
 
function Boid:steer( _target, _slowdown )
    local steer
    local desired = _target - self.location
 
    local dist = desired:len()
 
    if ( dist > 0 ) then
        desired = desired:normalize()
 
        if ( _slowdown and dist < 60 ) then
            desired = desired * ( self.maxSpeed * (dist / 60) )
            if ( dist < 10 ) then
                self.hasArrive = true
            end
        else 
            desired = desired * self.maxSpeed
        end
 
        steer = desired - self.velocity
        steer = vec2( 
            math.min(steer.x, self.maxForce ), 
            math.min(steer.y, self.maxForce) 
        )
    else 
        steer = vec2( 0, 0 )
    end
 
    return steer
end
 
function Boid:seek( _target ) 
    self.acceleration = self.acceleration + self:steer( _target, false ) 
end
 
function Boid:arrive(  _target ) 
    self.acceleration = self.acceleration + self:steer( _target, true )
end
 
function Boid:flee( _target )
    acceleration = acceleration - self:steer( _target, false ) 
end
 
function Boid:wander()
    local wanderR     = 5
    local wanderD     = 100
    local change     = 0.05
 
    self.wanderTheta = self.wanderTheta +  self.wanderTheta math.random( -change, change )
 
    local circleLocation = self.velocity
    circleLocation = circleLocation:normalize()
    circleLocation = (circleLocation * wanderD) + self.location
 
    local circleOffset = vec2(
        wanderR * math.cos(self.wanderTheta), 
        wanderR * math.sin(self.wanderTheta ) 
    )
    local target = circleLocation + circleOffset
 
    self:seek( target )
end
 
function Boid:evade(  _target ) 
    local lookAhead = self.location:dist( _target ) / (self.maxSpeed * 2)
    local predictedTarget = vec2( _target.x - lookAhead, _target.y - lookAhead )
    self:flee( predictedTarget )
end
 
 
 
 
Flagellum = class()
--[[    
    int numNodes
 
    float[][] spine
 
    float MUSCLE_RANGE     = 0.15
    float muscleFreq    = 0.08
 
    float sizeW, sizeH
    float spaceX, spaceY
    float theta
    float count
]]--    
 
function Flagellum:init( _sizeW, _sizeH,  _numNodes ) 
    self.MUSCLE_RANGE    = 0.15
    self.muscleFreq      = 0.08
    self.sizeW           = _sizeW
    self.sizeH           = _sizeH
    self.numNodes        = _numNodes
    self.spine           = {} --new float[numNodes][2]
    self.spaceX          = _sizeW / (_numNodes + 1.0)
    self.spaceY          = _sizeH / 2.0
    self.count           = 0
    self.theta           = math.pi
    self.thetaVel        = 0
 
    -- Initialize spine positions
    for n = 0, _numNodes-1 do
        local x = self.spaceX * n
        local y = self.spaceY
        self.spine[n] = {}
        self.spine[n][0] = x
        self.spine[n][1] = y
    end
    -- create mesh
    self.m = mesh()
end
 
 
function Flagellum:swim() 
    self.spine[0][0] = math.cos( self.theta )
    self.spine[0][1] = math.sin( self.theta )
 
    self.count = self.count + self.muscleFreq:len()
    local thetaMuscle = self.MUSCLE_RANGE * math.sin( self.count )
 
    self.spine[1][0] = -self.spaceX * math.cos( self.theta + thetaMuscle ) + self.spine[0][0]
    self.spine[1][1] = -self.spaceX * math.sin( self.theta + thetaMuscle ) + self.spine[0][1]
 
    for  n = 2 , self.numNodes - 1 do
        local x    = self.spine[n][0] - self.spine[n - 2][0]
        local y = self.spine[n][1] - self.spine[n - 2][1]
        local l = math.sqrt( (x * x) + (y * y) )
 
        if ( l > 0 ) then
            self.spine[n][0] = self.spine[n - 1][0] + (x * self.spaceX) / l
            self.spine[n][1] = self.spine[n - 1][1] + (y * self.spaceX) / l
        end
    end
end
 
function Flagellum:draw()
    self.m:draw()
    --self:debugRender()
end
 
function Flagellum:debugRender()
    for n = 0 ,self.numNodes - 1  do
        stroke( 0 )
        if ( n < self.numNodes - 1 ) then
            line( self.spine[n][0], self.spine[n][1], self.spine[n + 1][0], self.spine[n + 1][1] )
        end
        fill( 90 )
        ellipse( self.spine[n][0], self.spine[n][1], 6, 6 )
    end
end

Video:
www.youtube.com/watch?v=PRcYsOdSO5k

Capture:
image

Tagged:

Comments

  • Posts: 437
  • Posts: 372

    Tried it out but have one question how did you make the fish in the video move randomly mine only goes in one direction.

  • Posts: 437

    Add

    function touched(touch)
     fish.mousePosition = vec2(touch.x,touch.y)
    end
    

    And Just touch the screen

  • Posts: 372

    Cool thanks!!

  • Posts: 139

    Can't realy use can't you make it paste able so the tabs go to the correct places and also make an example fish to start with? If you can

  • Posts: 437

    OK @Mnjk78lg

    Start a new project. Put this in the Main tab:

    -- Fish example
    
    function setup()
        fish = Fish(
            vec2(WIDTH/2,HEIGHT/2), 
            math.random(2.0, 2.5 ), 
            0.9
        )
    
    end
    
    function draw()
      fish:draw()
    end
    
    function touched(touch)
     fish.mousePosition = vec2(touch.x,touch.y)
    end
    

    create a tab named Fish and paste all the code from the gist in there:
    https://gist.github.com/juaxix/5956606

    the order, first the Fish tab, as it contains the definition of the fish class, ;)

  • Posts: 688

    @juaxix - I love anything "procedural" to do with content generation, it really gives an extra edge (and put's the power back in the programmers hands).

    Thanks for sharing.

  • Posts: 437

    Maybe you'll like this procedural world too, from the game I'm writing
    www.youtube.com/watch?v=cKYPzIs1rFA

  • Posts: 688

    I love the graphical style, the art looks really good - the land (wave) scape looks like it could do with being a bit smoother - as it's a fish why not have the fish swim just under the water and have some physics that you have to fight against to get the fish to swim down and then use the buoyancy as it rises to increase the speed (if that makes sense).

  • Posts: 437

    The smoothness of the waves can be set with a param, look at the code in this post:
    http://twolivesleft.com/Codea/Talk/discussion/1267/little-jumping-thing

    I thought the same with the swimming but this fish is a fly-fish, look at the story, so he can fly over the seas ;)

Sign In or Register to comment.