but how could we use sine to create smooth animations
Introduction
This tutorial/article is going to be somewhat underdeveloped and possibly even wrong sometimes, but I've been asked to explain this twice and I know a lot of people have trouble with it. Please leave questions and clarifications in the comments and they may be incorporated into the copy. To get the most use out of this tutorial, you will need to attempt to understand the math concepts and shapes behind it, NOT just the specific code. By copying without understanding, you do not learn. It should help answer questions like: (Movement Section) How do I get a sprite to follow the player? How do I move a bullet at an angle? (Smoothing Section) What can I do to improve the aesthetic quality of my game? How can I smoothly interpolate between two values? Why radians? (General) What is SIN() useful for? How does this crap even work? My teacher didn't teach well Why everything gotta cost radians? You're probably here because, like I got in middle school, your education (or future education on) trigonometry consisted of definitions and memorization, and you know there's SOMETHING about angles that you need to work with in your game but you don't really get it. Trigonometry is a scary word that means "math about angles and triangles."Movement
All of the useful stuff we can do with it regarding movement is based on this concept of the "unit circle," a circle with a radius of 1 unit centered around the origin (the point (0,0) on a coordinate system). Angles are counted counter-clockwise.![](http://kland.smilebasicsource.com/i/ijkme.png)
![](http://kland.smilebasicsource.com/i/euoqb.png)
![](http://kland.smilebasicsource.com/i/gxcef.png)
![](http://kland.smilebasicsource.com/i/rcysp.png)
![](http://kland.smilebasicsource.com/i/euoqb.png)
VAR ANG = RAD(90) 'SIN() and COS() take "radians," 'we have to transform our angle in degrees 'to radians with RAD() ?SIN(ANG) ' 1 ?COS(ANG) ' 0Remember that 90° is perfectly north on the circle, a Y-offset of 1 and an X-offset of 0. That's right. SIN() gives us the Y component of an angle, and COS() gives us the X component. Now it's starting to make sense why we'd want this, right? "But Miss Yuuka," you say, "how do we find the angle?"
![](http://kland.smilebasicsource.com/i/laouw.png)
![](http://kland.smilebasicsource.com/i/bktbc.png)
C_X = TARGET_X - ENEMY_X C_Y = TARGET_Y - ENEMY_YAll we do is find the difference between the points. In fact, we're calculating the lengths of the legs of the triangle right now, so why bother with ATAN or any of that? Because if we used these distances, we'd jump right to the other blob! We have to normalize them to a travel distance of exactly 1... just like that unit circle we had earlier. So given these distances, we can plug them into ATAN to get the angle (in radians), and then from there, use SIN and COS to get the horizontal and vertical components of our movement.
'remember, Y comes first ANG = ATAN(C_Y, C_X) STEP = 1 ' distance to move, "speed" if you want ENEMY_X = ENEMY_X + COS(ANG) * STEP ENEMY_Y = ENEMY_Y + SIN(ANG) * STEPIf we had this in a real loop with SPOFS, the "enemy" would chase the player around. Here's a full example where a sprite shoots something at you (without collision or anything)
SPSET 0,84 SPSET 1,5 VAR X, Y, DX, DY, EX=200, EY=120, BX, BY, ANG SPOFS 1, EX, EY WHILE 1 'player movement STICK OUT DX,DY INC X, DX*2 DEC Y, DY*2 SPOFS 0,X,Y 'the relevant part IF !SPUSED(2) THEN 'keep shooting, see L27 SPSET 2, 257 SPOFS 2, EX, EY ANG = ATAN(Y - EY, X - EX) 'get angle from difference in position 'but calculate it once; this is a bullet ENDIF SPOFS 2 OUT BX, BY 'get current coordinates 'get horizontal part with COS BX = BX + 5 * COS(ANG) ' *5 for SPEEEED 'get vertical part with SIN BY = BY + 5 * SIN(ANG) SPOFS 2, BX, BY IF SQR(POW(BX,2)+POW(BY,2)) > 512 THEN SPCLR 2 'destroy it after it goes far enough VSYNC WENDThat's about it.
Smooth Transitions
In Section 2 we exploited one characteristic of the unit circle, its unit radius, to move a sprite based on a given angle. Here we'll exploit another property: it's a circle! If we were to move, say, 10 degrees from 0, what would the change in vertical position (or Δsin(θ)) be?![](http://kland.smilebasicsource.com/i/renkk.png)
?SIN(RAD(10)) - SIN(RAD(0)) 0.17364818 OKBut since it's a circle, the arc (a section of a circle) at the "top" should have less vertical change in the same 10 degrees.
![](http://kland.smilebasicsource.com/i/safvz.png)
?SIN(RAD(90)) - SIN(RAD(80)) 0.01519225 OKThat's almost a tenth less vertical change than the first 10 degrees! Okay now check out this graph of of sine:
![](http://kland.smilebasicsource.com/i/lsemu.png)
A Note On Radians
One unit for measuring angles that we've been using is degrees. Another common unit is radians. Actually, we've already been using them, by converting degrees with RAD(), but I haven't actually discussed this yet. One radian is equal to the length of the radius of the circle, measured along the perimeter.![](http://kland.smilebasicsource.com/i/zqojf.png)
![](https://upload.wikimedia.org/wikipedia/commons/4/4e/Circle_radians.gif)
π/6 = 30° π/4 = 45° π = 180° 3π/2 = 270° 2π = 360°And finally, to convert between degrees and radians: deg->rad: (π/180) * deg rad->deg: (180/π) * rad
Back to Interpolation
Previously on Sinewave: Pantsless: "The period of this function is two-pi radians! What are we going to do!?" "Our Subtraction Ray is no use! It fluctuates between 1 and -1!"![](http://kland.smilebasicsource.com/i/kscth.png)
SPSET 0,84 VAR X, Y = 120, DX = 5 WHILE 1 IF X <= 0 THEN DX=2.5 ELSEIF X >= 400 THEN DX=-2.5 ENDIF X = X + DX SPOFS 0, X, Y VSYNC WENDHere, the letter T bounces back and forth between the edges of the screen. However, this can be very awkward for actual game situations: imagine if this were supposed to model a bouncing ball. That wouldn't work at all. We can use SIN to ease the interpolation between the values, but first we'll need a general interpolation function:
'Linear interpolation (map t[0,1] to [min,max]) DEF LERP(T, MIN, MAX) RETURN MIN + T * (MAX - MIN) ENDThis function takes a percent value (0.00 to 1.00) and maps it to a position along the line from MIN to MAX. That means that anything that produces a value between 0 and 1 can be used for input... including the trigonometric functions.
more on LERP
![](http://kland.smilebasicsource.com/i/fwqvu.png)
SPSET 0,84 SPSET 1,88 VAR X, Y = 100, X2, Y2 = 140, DX, DX2 WHILE 1 'first sprite IF X <= 0 THEN DX=2.5 ELSEIF X >= 384 THEN DX=-2.5 ENDIF X = X + DX SPOFS 0, X, Y 'second sprite IF X2 <= 0 THEN DX2=2.5 ELSEIF X2 >= 384 THEN DX2=-2.5 ENDIF X2 = X2 + DX2 SPOFS 1, LERP(SIN(PI() * X2 / 384 ), 0, 384), Y2 VSYNC WEND 'Linear interpolation (map t[0,1] to [min,max]) DEF LERP(T, MIN, MAX) RETURN MIN + T * (MAX - MIN) ENDIn this sample, I've added a second sprite, which is controlled as the first one, with the exception of using LERP(SIN(PI() * X2 / 384 ), 0, 384) in place of X. Also, I changed all the "400" values to "384," just so that the 16x16 sprites stay on screen. So that's neat: we get this bouncing effect on it that looks pretty natural. You'll also notice that it moves exactly twice as fast as the other one. Time to play around with a simplified demo. First off, since sine is periodic, we don't actually need the "IF X2 <= 0..." check, assuming we want the motion to loop forever.
SPSET 0,88 VAR X, Y = 120, DX = 2.5 WHILE 1 X = X + DX SPOFS 0, LERP(SIN(PI()*X/384), 0, 384), Y VSYNC WEND 'Linear interpolation (map t[0,1] to [min,max]) DEF LERP(T, MIN, MAX) RETURN MIN + T * (MAX - MIN) ENDThis code, however, does NOT have the same effect as the earlier sample!
![](http://kland.smilebasicsource.com/i/regcp.png)
SPSET 0,88 VAR X = 0, Y = 120, DX = 2.5 WHILE 1 X = X + DX SPOFS 0, LERP(SIN(PI()*X/400), 192, 384), Y VSYNC WEND 'Linear interpolation (map t[0,1] to [min,max]) DEF LERP(T, MIN, MAX) RETURN MIN + T * (MAX - MIN) ENDThat's about as much as I know. Figuring out how to apply it is up to you, but there's one more note within the scope of this tutorial: The graph of cosine is phase-shifted π/2 from the graph of sine:
![](http://www.bbc.co.uk/bitesize/higher/maths/images/radians2_graphs02.gif)