I have a sprite that needs to be rotated at a custom angle. I know there is a math.rotate() function, but can this function be transferred to a sprite or image? I need to be able to individually rotate many sprites, all at custom rotations. Finding the right rotation value will be difficult in it self, but I want to know how rotating sprites works before I get started.
Hello @edat44. One thing you can do, is illustrated by the code below (updated: comments added):
--
-- Sprite
--
-- Create a class to hold data about the sprite
Sprite = class()
-- Initialise a sprite with its image
function Sprite:init(img)
self.image = img
self.x = 0
self.y = 0
self.angle = 0
end
-- The sprite can draw itself
function Sprite:draw()
pushMatrix() -- Preserve the current matrix
pushStyle() -- Preserve the current style
resetMatrix() -- Reset the matrix: 'origin' in the bottom left corner
resetStyle() -- Reset the style
translate(self.x, self.y) -- Shift (translate) origin of Viewer by (x, y)
rotate(self.angle) -- Rotate the Viewer about that origin
spriteMode(CENTER)
sprite(self.image, 0, 0) -- Draw at the (shifted, rotated) origin
popStyle() -- Restore the style to what it was
popMatrix() -- Restore the matrix to what it was
end
supportedOrientations(LANDSCAPE_ANY)
function setup()
mySprite = {}
local img = readImage("Planet Cute:Character Princess Girl")
for i = 1, 10 do
mySprite[i] = Sprite(img)
mySprite[i].x = math.random(WIDTH)
mySprite[i].y = math.random(HEIGHT)
mySprite[i].angle = math.random(360)
end
end
function draw()
background(0)
for i = 1, 10 do
mySprite[i].angle = mySprite[i].angle +
DeltaTime * math.random(90, 270)
mySprite[i]:draw()
end
end
. @edat44 and for those interested, another way is to use mesh() instead of sprites.
Pic = class()
function Pic:init(src)
self.surface = mesh()
self.surface.texture = src
self.w, self.h = spriteSize(src)
self.id = self.surface:addRect(0, 0, self.w, self.h)
end
function Pic:draw(x, y, r)
self.surface:setRect(self.id, x, y, self.w, self.h, r)
self.surface:draw()
end
function setup()
img = Pic("Documents:mySpriteTexture")
end
function draw()
background(0)
img:draw(CurrentTouch.x, CurrentTouch.y, math.sin(ElapsedTime))
end
Not tested but this should work.
Cheers
Thanks @mpilgrem. I was even able to use code with the new parameter and tween features in the newest Codea update to smoothly animate the sprite.
Sorry to necro this thread, but does anyone have any thoughts on the differences in performance between these two methods?
Edit: I’ve just tested them both, and the Sprite version is faster - perhaps due to the Pic version requiring extra parameters, even if you don’t want to scale.
Generally, meshes outperform sprites, not least because sprites are actually drawn using meshes, behind the scenes. The difference may not be noticeable unless you have a lot of images.
If you’re going to do any serious graphics, though, I would focus on meshes.
That’s really weird. Using close-to-identical code, I can get 400-odd sprites, but only 300-or-so meshes! I’m a bit baffled.
@Madrayken, if you’re creating meshes every frame and drawing loads of different meshes then it can get slow, I find using addRect and multiple rectangles much faster than 100 meshes with 1 rectangle or a sprite. Creating rectangles for one mesh every frame is a lot faster than creating a whole new mesh
Clearly, I need to understand meshes better. Thanks.
Posting code below. No idea why the formatting has gone so horribly wrong…
--# Main
NUMSPRITES = 5
GRAVITY = -9.8
HALF_PI = math.pi * 0.5
-- Use this function to perform your initial setup
function setup()
spriteSet = {}
for i = 1, NUMSPRITES do
spriteSet[i] = Pic()
--spriteSet[i] = Sprite()
end
background(100, 120, 160)
font("Georgia")
displayMode(FULLSCREEN)
fill(255)
fontSize(20)
textWrapWidth(70)
spriteMode()
end
-- This function gets called once every frame
function draw()
background(93, 93, 111, 255)
strokeWidth(1)
stroke(255)
noSmooth()
for i = 1, NUMSPRITES do
spriteSet[i]:update()
spriteSet[i]:draw()
end
-- Put an FPS counter in the corner of your screen
fill(255,255,255)
textMode(CORNER)
fps = string.format("%.2f", 1/DeltaTime)
text(math.ceil(fps),10,10)end
function touched(touch)
print("Hello World!")
end
--# Pic
Pic = class()
function Pic:init()
src = "Dropbox:Head"
self.pos = vec2(math.random(WIDTH), math.random(HEIGHT))
self.velocity = vec2(math.random(RANDOM_X_VEL * 2) - RANDOM_X_VEL, 0)
self.angle = math.random(math.deg(360))
self.surface = mesh()
self.surface.texture = src
self.w, self.h = spriteSize(src)
self.id = self.surface:addRect(0, 0, self.w, self.h)
end
function Pic:update()
self.velocity.y = self.velocity.y + GRAVITY * DeltaTime
self.pos = self.pos + self.velocity
if self.pos.y < 0 then
self.velocity.y = self.velocity.y * -1
self.pos.y = self.pos.y * -1
end
if self.pos.x < 0 then
self.velocity.x = self.velocity.x * -1
self.pos.x = self.pos.x * -1
elseif self.pos.x > WIDTH then
self.velocity.x = self.velocity.x * -1
self.pos.x = WIDTH - (self.pos.x - WIDTH)
end
self.angle = math.atan2(self.velocity.y, self.velocity.x) - HALF_PI
end
function Pic:draw()
self.surface:setRect(self.id, self.pos.x, self.pos.y, self.w, self.h, self.angle)
self.surface:draw()
end
--# Sprite
Sprite = class()
RANDOM_X_VEL = 10
function Sprite:init()
self.pos = vec2(math.random(WIDTH), math.random(HEIGHT))
self.velocity = vec2(math.random(RANDOM_X_VEL * 2) - RANDOM_X_VEL, 0)
self.angle = math.random(math.deg(360))
end
function Sprite:update()
self.velocity.y = self.velocity.y + GRAVITY * DeltaTime
self.pos = self.pos + self.velocity
if self.pos.y < 0 then
self.velocity.y = self.velocity.y * -1
self.pos.y = self.pos.y * -1
end
if self.pos.x < 0 then
self.velocity.x = self.velocity.x * -1
self.pos.x = self.pos.x * -1
elseif self.pos.x > WIDTH then
self.velocity.x = self.velocity.x * -1
self.pos.x = WIDTH - (self.pos.x - WIDTH)
end
self.angle = math.atan2(self.velocity.y, self.velocity.x)
self.angle = math.deg(self.angle) - 90
end
function Sprite:draw()
pushMatrix() -- ensure we don't ruin the matrix
translate(self.pos.x, self.pos.y)
rotate(self.angle)
sprite("Dropbox:Head", 0, 0)
popMatrix()
end
function Sprite:touched(touch)
-- Codea does not automatically call this method
end
@Madrayken - I’ve fixed your code formatting above. The trick is to put three ~ on the line before your code, and three more on the line under your code.
You can make the sprites faster still by reading the image into a variable in setup, and spriting that in draw, ie in setup
img = readImage(“Dropbox:Head”)
and in draw
sprite(img, 0, 0)
I’m not sure why the meshes are slower at this stage
This code runs meshes really fast, by putting all the images in a single mesh (because they all use the same texture image). This is much faster because each mesh has a setup process.
It also explains why the sprites were faster, because Codea batches sprites behind the scenes - I’m guessing it creates a single mesh from all of them (if you weren’t aware, sprites are actually meshes as well).
--# Main
NUMSPRITES = 300
GRAVITY = -9.8
HALF_PI = math.pi * 0.5
RANDOM_X_VEL=10
-- Use this function to perform your initial setup
function setup()
img=readImage("Planet Cute:Character Pink Girl")
mm=mesh()
mm.texture=img
spriteSet = {}
for i = 1, NUMSPRITES do
spriteSet[i] = Pic(mm)
end
background(100, 120, 160)
font("Georgia")
displayMode(FULLSCREEN)
fill(255)
fontSize(20)
textWrapWidth(70)
spriteMode()
end
function draw()
background(93, 93, 111, 255)
strokeWidth(1)
stroke(255)
noSmooth()
for i = 1, NUMSPRITES do
spriteSet[i]:update()
end
mm:draw()
-- Put an FPS counter in the corner of your screen
fill(255,255,255)
textMode(CORNER)
fps = string.format("%.2f", 1/DeltaTime)
text(math.ceil(fps),10,10)end
--# Pic
Pic = class()
function Pic:init(m)
self.pos = vec2(math.random(WIDTH), math.random(HEIGHT))
self.velocity = vec2(math.random(RANDOM_X_VEL * 2) - RANDOM_X_VEL, 0)
self.angle = math.random(math.deg(360))
self.surface=m
self.w, self.h = spriteSize(src)
self.id = m:addRect(0, 0, self.w, self.h)
end
function Pic:update()
self.velocity.y = self.velocity.y + GRAVITY * DeltaTime
self.pos = self.pos + self.velocity
if self.pos.y < 0 then
self.velocity.y = self.velocity.y * -1
self.pos.y = self.pos.y * -1
end
if self.pos.x < 0 then
self.velocity.x = self.velocity.x * -1
self.pos.x = self.pos.x * -1
elseif self.pos.x > WIDTH then
self.velocity.x = self.velocity.x * -1
self.pos.x = WIDTH - (self.pos.x - WIDTH)
end
--self.angle = math.atan2(self.velocity.y, self.velocity.x) - HALF_PI
self.angle = math.atan2(self.velocity.y, self.velocity.x)
self.angle = math.deg(self.angle) - 90
self.surface:setRect(self.id, self.pos.x, self.pos.y, self.w, self.h, self.angle)
end
Thanks sooooo much, Ignatz. I was tearing out my hair over both those issues!
As an aside - is this ‘make them all one mesh’ technique a good one for a simple tile-based background, or do you have a better suggestion?
If the background isn’t ever going to change, just draw it all onto a single image at the beginning with setContext, then sprite that in draw.
If is a dynamic background and you do have to draw it fully all the time, then yes, a single mesh is quicker than a whole lot of meshes. However, if each tile has its own image, you can’t combine them in one mesh (because a mesh can only have one texture image) unless you cram all the tiles onto one image and select the right piece to use for each tile.
There was quite a lot of discussion on this for a game called RPGMaker - it might be worth searching the forum for that.
Here’s another example of rotating sprites. Let it run for at least 2 minutes.
displayMode(FULLSCREEN)
function setup()
x=100
y=100
tab={}
table.insert(tab,sp(WIDTH/2+50,HEIGHT/2+50))
for z=1,40 do
table.insert(tab,sp(x,y))
end
end
function draw()
background(40, 40, 50)
for a,b in pairs(tab) do
b:draw()
end
end
sp=class()
function sp:init(x,y)
self.x=x
self.y=y
self.ang=0
end
function sp:draw()
translate(self.x,self.y)
rotate(self.ang)
sprite("Planet Cute:Heart",0,0)
self.ang = self.ang + .05
end
Here’s the above program, but the rotation angle is based on where you slide your finger up or down the screen.
EDIT: Added math.floor()
displayMode(FULLSCREEN)
function setup()
dt=0
x=100
y=100
tab={}
table.insert(tab,sp(WIDTH/2+50,HEIGHT/2+50))
for z=1,40 do
table.insert(tab,sp(x,y))
end
end
function draw()
background(40, 40, 50)
fill(255)
text(dt,WIDTH/2,HEIGHT-50)
for a,b in pairs(tab) do
b:draw()
end
end
function touched(t)
dt=math.floor(180/HEIGHT*t.y)
end
sp=class()
function sp:init(x,y)
self.x=x
self.y=y
end
function sp:draw()
translate(self.x,self.y)
rotate(dt)
sprite("Planet Cute:Heart",0,0)
end