So Charlie has this tutorial and source code in BlitzMax: http://www.charliesgames.com/wordpress/?p=441
I ported it to glBasic, have fun!
(http://dl.dropbox.com/u/292449/blobmonster/screenshot.png)
// --------------------------------- //
// Project: blobmonster
// Start: Friday, February 04, 2011
// IDE Version: 8.200
SETCURRENTDIR("Media") // seperate media and binaries?
//ported to glBasic by Bence Dobos, 2011
//How to make an Irukandji style blob monster.
//By Charlie Knight, 2009/2010
//http://www.charliesgames.com
//
//This code is public domain. Feel free to use as you please.
//If you use the code as a basis for your own blob monsters, let me know! I'd love to
//see what you came up with!
//
//The code is written using the Blitzmax language, but it should be fairly easy to port
//to C++/Java/Whatever with a suitable graphics library.
//
//the image blob.png can be found at http://www.charliesgames.com/wpimages/blob.png
//
//Cheers
//Charlie
GLOBAL KEY_ESCAPE=1
GLOBAL blob=0
GLOBAL w, h
//load the image
LOADSPRITE "blob.png", blob
GETSPRITESIZE blob, w, h
//create a blobMonster object
LOCAL test AS blobMonster
test.Create(10, 10)
//main loop
WHILE KEY(KEY_ESCAPE)=0
//update and draw the blobmonster
CLEARSCREEN
test.Update()
test.Draw()
SHOWSCREEN
WEND
END
//Finished!
//simple class (type in glBasic) to hold a 2d coordinate
TYPE point
x#
y#
ENDTYPE
//here's the blob monster type
TYPE blobMonster
//x and y coords
x#
y#
//speed, try changing it
speed# = 1
//number of nodes along the body, try changing it to 100
segments# = 10
//array to hold the points along the body
tail[] AS point
time# = 0
//function that returns a new blob monster object. Blitzmax equivalent (kind of)
//of a constructor in C++/Java
FUNCTION Create AS blobMonster:inX#, inY#
DIM self.tail[self.segments]
//starting point of the blob monster
self.x = inX
self.y = inY
//give the tail some coordinates, just make them the same as the main x and y for now
FOR i = 0 TO self.segments - 1
self.tail[i].x# = inX
self.tail[i].y# = inY
NEXT
RETURN self
ENDFUNCTION
FUNCTION Update%:
//time is a bit misleading, it's used for all sorts of things
INC self.time#, self.speed#
//here the x and y coordinates are updated.
//this uses the following as a basic rule for moving things
//around a point in 2d space:
//x=radius*cos(angle)+xOrigin
//y=raduis*sin(angle)+yOrigin
//this basically is the basis for anything that moves in this example
//
//the 2 lines of code below make the monster move around, but
//you can change this to anything you like, try setting x and y to the mouse
//coordinates for example
self.y = (15 * COS(self.time * -6)) + (240 + (180 * SIN(self.time * 1.3)))
self.x = (15 * SIN(self.time * -6)) + (320 + (200 * COS(self.time / 1.5)))
//put the head of the tail at x,y coords
self.tail[0].x = self.x
self.tail[0].y = self.y
//update the tail
//basically, the points don//t move unless they//re further that 7 pixels
//from the previous point. this gives the kind of springy effect as the
//body stretches
FOR i = 1 TO self.segments - 1
//calculate distance between the current point and the previous
LOCAL distX# = (self.tail[i - 1].x - tail[i].x)
LOCAL distY# = (self.tail[i - 1].y - tail[i].y)
LOCAL dist# = SQR(distX * distX + distY * distY)
//move if too far away
IF dist > 7
//the (distX*0.2) bit makes the point move
//just 20% of the distance. this makes the
//movement smoother, and the point decelerate
//as it gets closer to the target point.
//try changing it to 1 (i.e 100%) to see what happens
self.tail[i].x = self.tail[i].x + (distX * (0.3))
self.tail[i].y = self.tail[i].y + (distY * (0.3))
ENDIF
NEXT
RETURN FALSE
ENDFUNCTION
FUNCTION Draw:
//time to draw stuff!
//this sets the blend mode to LIGHTBLEND, or additive blending, which makes
//the images progressively more bright as they overlap
//ALPHAMODE LIGHTBLEND
LOCAL color=RGB( 0, 200, 150 )
LOCAL scale=1
//###########
//draw the main bit of the body
//begin looping through the segments of the body
FOR i = 0 TO self.segments - 1
//set the alpha transparency vaue to 0.15, pretty transparent
ALPHAMODE 0.15
//the (0.5*sin(i*35)) bit basically bulges the size of the images being
//drawn as it gets closer to the center of the monsters body, and tapers off in size as it gets
//to the end. try changing the 0.5 to a higher number to see the effect.
scale = 1 + (0.5 * SIN(i * 35))
//draw the image
PolySprite(blob, self.tail[i].x, self.tail[i].y, scale, scale, color, 0.5, 0.5, 0, w, h)
//this next chunk just draws smaller dots in the center of each segment of the body
ALPHAMODE 0.8
scale = 0.1
PolySprite(blob, self.tail[i].x, self.tail[i].y, scale, scale, color, 0.5, 0.5, 0, w, h)
NEXT
//#########################
//draw little spikes on tail
color = RGB( 255, 255, 255 )
//note that the x and y scales are different
LOCAL scalex = 0.6
LOCAL scaley = 0.1
LOCAL rot=0
//move the image handle to halfway down the left edge, this'll make the image
//appear to the side of the coordinate it is drawn too, rather than the
//center as we had for the body sections
//rotate the 1st tail image. basically, we//re calculating the angle between
//the last 2 points of the tail, and then adding an extra wobble (the 10*sin(time*10) bit)
//to make the pincer type effect.
rot = 10 * SIN(self.time * 10) + calculateAngle(self.tail[self.segments - 1].x, self.tail[self.segments - 1].y, self.tail[self.segments - 5].x, self.tail[self.segments - 5].y) + 90
PolySprite(blob, self.tail[self.segments - 1].x, self.tail[self.segments - 1].y, scalex, scaley, color,0, 0.5, rot, w, h)
//second tail image uses negative time to make it move in the opposite direction
rot = 10 * SIN(-self.time * 10) + calculateAngle(self.tail[self.segments - 1].x, self.tail[self.segments - 1].y, self.tail[self.segments - 5].x, self.tail[self.segments - 5].y) + 90
PolySprite(blob, self.tail[self.segments - 1].x, self.tail[self.segments - 1].y, scalex, scaley, color,0, 0.5, rot, w, h)
//#####################
//draw little fins/arms
ALPHAMODE 1
//begin looping through the body sections again. Note that we don't want fins
//on the first and last section because we want other things at those coords.
FOR i = 1 TO self.segments - 2
//like the bulging body, we want the fins to grow larger in the center, and smaller
//at the end, so the same sort of thing is used here.
scalex = 0.1 + (0.6 * SIN(i * 30))
scaley = 0.05
//rotate the image. We want the fins to stick out sideways from the body (the calculateangle() bit)
//and also to move a little on their own. the 33 * Sin(time * 5 + i * 30) makes the
//fin rotate based in the i index variable, so that all the fins look like they//re moving
//one after the other.
rot = 33 * SIN(self.time * 5 + i * 30) + calculateAngle(self.tail[i].x, self.tail[i].y, self.tail[i - 1].x, self.tail[i - 1].y)
PolySprite( blob, self.tail[i].x, self.tail[i].y, scalex, scaley, color, 0, 0.5, rot, w, h)
//rotate the opposte fin, note that the signs have changes (-time and -i*30)
//to reflect the rotations of the other fin
rot = 33 * SIN(-self.time * 5 - i * 30) + calculateAngle(self.tail[i].x, self.tail[i].y, self.tail[i - 1].x, self.tail[i - 1].y) + 180
PolySprite( blob, self.tail[i].x, self.tail[i].y, scalex, scaley, color, 0, 0.5, rot, w, h)
NEXT
//###################
//Draw the eyes. These are just at 90 degrees to the head of the tail.
color = RGB( 255, 0, 0 )
scale = 0.6
ALPHAMODE 0.3
LOCAL ang# = calculateAngle(self.tail[0].x, self.tail[0].y, self.tail[1].x, self.tail[1].y)
PolySprite(blob, self.x + (7 * COS(ang + 50)), self.y + (7 * SIN(ang + 50)), scale, scale, color, 0.5, 0.5, 0.0, w, h)
PolySprite(blob, self.x + (7 * COS(ang + 140)), self.y + (7 * SIN(ang + 140)), scale, scale, color, 0.5, 0.5, 0.0, w, h)
color = RGB( 255, 255, 255 )
scale = 0.1
ALPHAMODE 0.5
PolySprite(blob, self.x + (7 * COS(ang + 50)), self.y + (7 * SIN(ang + 50)), scale, scale, color, 0.5, 0.5, 0.0, w, h)
PolySprite(blob, self.x + (7 * COS(ang + 140)), self.y + (7 * SIN(ang + 140)), scale, scale, color, 0.5, 0.5, 0.0, w, h)
//draw beaky thing
color = RGB( 0, 200, 155 )
scalex = 0.3
scaley = 0.1
ALPHAMODE 0.8
rot = ang + 95
PolySprite(blob, self.x, self.y, scalex, scaley, color, 0.0, 0.5, rot, w, h)
//yellow light
color = RGB( 255, 255, 0 )
ALPHAMODE 0.2
scale = 4
PolySprite(blob, self.x, self.y, scale, scale, color, 0.5, 0.5, 0, w, h)
//Finished!
ENDFUNCTION
ENDTYPE
//This function calculates and returns the angle between two 2d coordinates
FUNCTION calculateAngle#:x1#,y1#,x2#,y2#
LOCAL theX#=x1-x2
LOCAL theY#=y1-y2
LOCAL theAngle#=-ATAN(theX,theY)
RETURN theAngle
ENDFUNCTION
//Draw and colorize a sprite. You can set the handle point, rotation, scale and color of the sprite
//Written by Bence Dobos
//spr - the ID of the sprite
//scalex, scaley - the ratio of the sprite scale [0.0-inf]
//color - 0xffffff is the normal color
//xc,yc - the ratio of the handle point of the sprite [0.0-1.0], this point is used to draw the sprite, rotate and scale. 0,0 is the top-left point, 0.5,0.5 is the center point
//rot - rotation of the sprite [0-360]
//w,h - width, height of the sprite in pixel, if not specified it's determined automatically
FUNCTION PolySprite: spr, x, y, scalex, scaley, color=0xffffff, xc=0.0, yc=0.0, rot=0, w=-1, h=-1
IF w=-1
GETSPRITESIZE spr, w, h
ENDIF
LOCAL w2=w*scalex
LOCAL h2=h*scaley
LOCAL xcenter=x+xc*w2
LOCAL ycenter=y+yc*h2
LOCAL xr1=-xc*w2
LOCAL yr1=-yc*h2
LOCAL xr2=(1.0-xc)*w2
LOCAL yr2=-yc*h2
LOCAL xr3=(1.0-xc)*w2
LOCAL yr3=(1.0-yc)*h2
LOCAL xr4=-xc*w2
LOCAL yr4=(1.0-yc)*h2
LOCAL x1, y1, x2, y2
LOCAL x3, y3, x4, y4
IF rot<>0
LOCAL sp = SIN(rot)
LOCAL cp = COS(rot)
x1 = xr1*cp-yr1*sp+x
y1 = xr1*sp+yr1*cp+y
x2 = xr2*cp-yr2*sp+x
y2 = xr2*sp+yr2*cp+y
x3 = xr3*cp-yr3*sp+x
y3 = xr3*sp+yr3*cp+y
x4 = xr4*cp-yr4*sp+x
y4 = xr4*sp+yr4*cp+y
ELSE
x1=xr1+x
y1=yr1+y
x2=xr2+x
y2=yr2+y
x3=xr3+x
y3=yr3+y
x4=xr4+x
y4=yr4+y
ENDIF
STARTPOLY spr
POLYVECTOR x1, y1, 0, 0, color
POLYVECTOR x2, y2, 0, h, color
POLYVECTOR x3, y3, w, h, color
POLYVECTOR x4, y4, w, 0, color
ENDPOLY
ENDFUNCTION
Edit: Missing beaky thing, fixed.
[attachment deleted by admin]
Heheh. Cute.
Cute? See in motion. I have nithmares with it. :O
Very cool effect! :good:
It would look great in a game.
Or to apply to a caterpillar animation. (By using some of the code of course, everything but the drawing I guess!)
It's neat how the only external graphic is the 'blob' circle and how it's used to create the creature part by part.
wow super effect :blink:
That is wonderfully organic. Clever stuff. :)
Quote from: Ian Price on 2011-Feb-11
That is wonderfully organic. Clever stuff. :)
Yeah, organic it is. Even when it twists in reversals or half reversals it is very life-like.
Stuff like this would be great in a rehash of the first level of Spore. That was the most fun part of that game imo.
The beak beetween its two eye was missing. I added it and changed the first post.
VERY GOOD!!!
:good:
Wow! Thats a really nice routine. Wish i had paid more attention to maths at school MANY MANY years ago :)
Very nice!
Wow, that is really very cool. ;)
I like that style!
I really need to find a way to work that creature into a game
You are free to do it, just told to Charlie :) He wants to see what games are we make with his monsters: http://www.charliesgames.com/wordpress/?p=441
Nice. Here's my first idea:
Hi I try a simple modification, but I like too much, on Alphamode, I think is for polyvertor apply a velua about 0.7 or 0.8, well you have a cool effect like a gum, it´s like seem a viscosity fluid, the effect is really cool. Try it, in my opinion a Like more than the original, but it´s only my personal opinion.
Kind Regards,
Iván J.
Kitty: That Wii mini-game is a surprising amount of fun I always thought.
Albert: That's seriously cool. I love the little guy. Reminds me a bit of some of the 'fish' you find in the deeper parts of the ocean with luminescent bits.
Change part of the the main code to this:
//create a blobMonster object
GLOBAL test AS blobMonster
test.Create(10, 10)
SETLOOPSUB "GLB_ON_LOOP" // - GLBasic V11 calls this at the END of "main"
//main loop
@SUB GLB_ON_LOOP: // this is called by the framework after the "main" init module
//update and draw the blobmonster
CLEARSCREEN
test.Update()
test.Draw()
SHOWSCREEN
ENDSUB
Compile to HTML5 and you get this:
https://dl.dropboxusercontent.com/u/292449/blobmonster.html
For a little HTML5 benchmark test modify tha main code again to this:
//create a blobMonster object
GLOBAL test[] AS blobMonster
CONSTANT numBlobs=10
REDIM test[numBlobs]
FOR i=0 TO numBlobs-1
test[i].Create(RND(300), RND(300), RND(1000))
NEXT
SETLOOPSUB "GLB_ON_LOOP" // - GLBasic V11 calls this at the END of "main"
//main loop
@SUB GLB_ON_LOOP: // this is called by the framework after the "main" init module
//update and draw the blobmonster
CLEARSCREEN
FOR i=0 TO numBlobs-1
test[i].Update()
test[i].Draw()
NEXT
SHOWSCREEN
ENDSUB
and blobMonster.Create to this:
//function that returns a new blob monster object. Blitzmax equivalent (kind of)
//of a constructor in C++/Java
FUNCTION Create AS blobMonster:inX#, inY#, inTime#
DIM self.tail[self.segments]
//starting point of the blob monster
self.x = inX
self.y = inY
self.time = inTime
//give the tail some coordinates, just make them the same as the main x and y for now
FOR i = 0 TO self.segments - 1
self.tail[i].x# = inX
self.tail[i].y# = inY
NEXT
RETURN self
ENDFUNCTION
And you got this:
https://dl.dropboxusercontent.com/u/292449/blobmonster30.html