Move object from one point to another based on duration - c#

Been wrapping my head around this problem for a while and looking for solutions online to no effect.
Basically if I have a sprite for example located at (10,10) I want to move it to say (50,100) and the whole process to take 2 seconds or whatever duration I specify. What is the exact math behind this? I was using a distance based solution to determine speed but was just using a random modifier to control the process. I need something more precise to execute exactly over a set duration.
Any help on this issue would be greatly appreciated!

Assuming linear interpolation (i.e. moving in a straight line from start position to end position at a constant rate):
The vector from start to destination is destination - start, i.e. for your example (40,90).
If you want this to happen over two seconds you need to divide it by two to get the distance travelled per second, so (20,45) per second for your example.
To get the position at any given time, first record the start time and calculate the current time minus the start time in seconds. So if the animation started at 12:01:30.000 and it is now 12:01:31.500 then 1.5 seconds have past since the start of the animation.
To get the current location you add the start location to the movement per second vector * the time elapsed, so in my example:
(10,10) + (20,45) * 1.5 = (10,10) + (30, 67.5) = (40, 77.5)

It's just a thing of interpolation and time.
There is linear, sinus, quadratic, ...
Here is some more info and examples in actionscript: link

Take a closer look to jQuery's animation algorithms... maybe you can use some of the code.
http://code.jquery.com/jquery-1.6.1.js (search for "custom:" as a starting point).

You need a couple of pieces of information to do this, start location, end location, duration and elapsed time.
Here's an example in actionscript:
package {
import flash.utils.getTimer;
import flash.events.Event;
import flash.display.Shape;
import flash.geom.Point;
import flash.display.Sprite;
public class Mover extends Sprite {
private var circle :Shape;
private var start :Point;
private var end :Point;
private var duration :int;
public function Mover() {
// first we create something to move, like, a circle
circle = new Shape();
circle.graphics.beginFill(0xff00ff);
circle.graphics.drawCircle(0, 0, 20);
addChild(circle);
// start and end positions
start = new Point(0, 0);
end = new Point(100, 100);
// and finally, the duration, i'm using milliseconds
duration = 2000;
// this event handler will run each frame
addEventListener(Event.ENTER_FRAME, handleEnterFrame);
}
private function handleEnterFrame(event:Event):void {
// we figure out how much of the animation has elapsed by using getTimer
// should you want to use a start time, add it here
var progress:Number = getTimer() / duration;
// we need to clamp our progress so we don't under- or overshoot the target
if(progress < 0) progress = 0;
if(progress > 1) progress = 1;
// then it's a matter of setting the position
// we use the start position as a base and then gradually add in
// the difference between the start and the end
circle.x = start.x + (end.x - start.x) * progress;
circle.y = start.y + (end.y - start.y) * progress;
}
}
}
If you're not all that interested in the how and just want the results, I wholeheartedly recommend a tweening engine like TweenLite or any of the other myriad of them. Just stay clear of the one that comes with flash, it's a bit crap.

Related

How to get an accurate method of time passed using audioSettings.dspTime?

Thanks for looking and for any help / advice you might have.
I'm trying to sync audio with gameplay.
Is this an accurate method of finding out how much time has passed using audioSettings.dspTime?
I take a float representing time and multiply by song.clip.frequency.
float myChosenAmountOfTimeInSamples = myChosenAmountOfTime * song.clip.frequency;
float audioSecondsLater = Audio + myChosenAmountOfTimeInSamples;
And this onto audioSettings.dspTime * song.clip.frequency
float Audio = (float)AudioSettings.dspTime * song.clip.frequency;
float audioSecondsLater = Audio + myChosenAmountOfTimeInSamples;
I'm using this code to see if it works. As far as I can tell it happens 1 second later
In Update
if (Input.GetKeyDown (KeyCode.A)){
StartCoroutine(test ());
}
And the coroutine
IEnumerator test ()
{
float Audio = (float)AudioSettings.dspTime * song.clip.frequency;
float myChosenAmountOfTime = 1f;
float myChosenAmountOfTimeInSamples = myChosenAmountOfTime * song.clip.frequency;
float audioSecondsLater = Audio + myChosenAmountOfTimeInSamples;
print ("Audio seconds later: " + audioSecondsLater);
while (true)
{
Audio = (float)AudioSettings.dspTime * song.clip.frequency;
if (Audio >= audioSecondsLater)
{
print ("Audio Now: " + Audio); // Theoretically this is 1 second later
break;
}
yield return new WaitForSeconds(1 / 1000f);
}
}
I'm not entirely sure, but I think you're on the right track. I've done sync before against a single AudioClip, but I keep all my timing in samples. To check the current sample I examine AudioSource.timeSamples on the clip that is playing. I use REAPER to examine the audio file and figure out at which sample in the file the events happen, and compare that to timeSamples to see if my sync event has happened yet.
AudioSource also has a time property which is the playback position in seconds, though see note in the documentation about inaccuracy when used with compressed audio.

How can I increase the volume of a sound from unhearable (0db) to loud (60db)

I try to increase the noise by doing this :
public void maakgeluid() {
WaveOut waveOut = new WaveOut();
SineWaveOscillator osc = new SineWaveOscillator(44100);
for (short amplitude = 1; amplitude <500; amplitude+=1) {
Console.WriteLine("amplitude:" + amplitude);
for (int i = 1; i < 10; i++) {
Console.WriteLine(i);
osc.Frequency = 500;
osc.Amplitude = amplitude;
try {
waveOut.Init(osc);
} catch (Exception ) { Console.WriteLine("error"); }
waveOut.Play();
}
}
}
The purpose is to generate a sound, like when you go to the ear specialist and take a hearing test. So it should start very silently, and then slowly get loader.
But I have different problems :
i hear the sound immediately
the sound increases to fast
i use the loop with the i counter, to lengthen the duration of the sound, but i don't think that is the right way to do it.
the looping to increase the sound level stops to quickly but I don't see why?
THx
based on the following code
msdn.microsoft.com/en-us/magazine/ee309883.asp
The bel scale is logarithmic, so you need to do maths to convert between decibels and a sample scaling factor.
In digital (floating point) audio a full signal (i.e. a waveform that reaches from +1 to -1, peak to trough) is considered to be 0dB.
As such, you'd probably want to go from -60dB to 0dB.
The conversion is as follows (assuming signed audio over unsigned, as with 8-bit audio)
double sampleValue = 1.0d;
//every -3db represents an approximate halving of linear signal level
double decibelValue = -3.0d;
double linearScalingRatio = Math.Pow(10d, decibelValue/10d);
var newSampleValue = sampleValue * linearScalingRatio;
So now, newSampleValue is 0.501187233627272
Your current code keeps recreating WaveOut devices which is not a good idea. Open the soundcard once, and then feed a single signal to it that gradually increases in volume over time. One way you could do this is to use the SignalGenerator class to make the sin wave, then pass that through a FadeInSampleProvider to gradually fade it in:
var sg = new SignalGenerator(44100,1);
sg.Frequency = 500;
sg.Type = SignalGeneratorType.Sin;
var fadeIn = new FadeInOutSampleProvider(sg, true);
fadeIn.BeginFadeIn(20000); // fade in over 20 seconds
waveOut.Init(fadein);
waveOut.Play();
As spender rightly points out, 0dB is maximum, so this is going from negative infinity decibels up to 0dB over the duration of the fade-in time. If you wanted to make it start at -60dB, or for the ramp-up of the multiplier to not be linear, then you'd need to make your own custom ISampleProvider similar to FadeInOutSampleProvider and use that instead.

More Precise timer than Stopwatch?

I'm trying to have a stopwatch start and stop when recording positions for the Kinect:
//process x and y coordinates
public void calculateJoints(Skeleton skeleton)
{
Joint rightHand = skeleton.Joints[JointType.HandRight];
Joint leftHand = skeleton.Joints[JointType.HandRight];
rightX = rightHand.Position.X;
rightY = rightHand.Position.Y;
}
//start the stopwatch (tried to use a greater time between positions 1 and 5 vs 1 and 2
public void processJointsAndRepeat(Skeleton skeleton)
{
startTime();
while (numPositions < 5)
{
calculateJoints(skeleton);
numPositions++;
}
stopTime();
double tempTime = calculateTimeElapsed();
}
//calculate time in milliseconds
private double calculateTimeElapsed()
{
long milliseconds = stopWatch.ElapsedMilliseconds;
return (double)milliseconds;
}
But whenever I try to put in the x, y, and time values with time as the key, it throws an error for duplicate keys. When I retrieved the value of tempTime, it only showed 0.
Is this a problem with my code, or do I need a more precise stopwatch?
I realize that getting a time for something that is 30 fps is difficult, so if you have any other suggestions, that'd be great! I'm basically just trying to calculate the average velocities between points to adjust the playback speed of an audio file. Thanks!
Stopwatch is wrapper around timer with higerst resolution on regular Windows box. You can use less fancy functions to get higer than MS resolution by using Stopwatch.ElapsedTicks and Stopwatch.Frequency.
Note that your problem is probably not related to timers but rather some other code you did not show...

Implementing a method onto text in XNA

So I have started to mess around with XNA today and I am still learning C#. I am trying to produce a main menu for a game.
I have made a sprite font file and am producing the text I want. The code for this is:
spriteBatch.DrawString(font, ">://Start Game [1]", new Vector2(0, 0), Color.LimeGreen);
My question is that I have a method to make a typing effect from the "computer" (which I asked a question about a few days ago), but this is in C++. I have an idea how to convert it into C#, but even if I do convert the code correctly, how do I apply the method onto the text being created? Is their a more efficient way to print text in XNA?
The code for the typing effect in C++ is:
void typeOutput(string displayString){
for(int i = 0; i < displayString.length(); i++){
cout << displayString[i];
Sleep((rand() + 1)%typeSpeed);
}
}
There are various ways to do this which is discussed in this thread. One example from that thread is:
// our string will take 3 seconds to appear
private const float timerLength = 3f;
private float timer = 0f;
Then in your Draw method you add to the timer and use that to determine how much of the string to draw:
timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
// if the timer is passed timerLength, we just draw the whole string
if (timer >= timerLength)
{
spriteBatch.DrawString(myFont, myString, stringPosition, stringColor);
}
// otherwise we want to just draw a substring
else
{
// figure out how many characters to show based on
// the ratio of the timer to the timerLength
int numCharsToShow = (int)(myString.Length * (timer / timerLength));
string strToDraw = myString.Substring(0, numCharsToShow);
// now just draw the substring instead
spriteBatch.DrawString(myFont, strToDraw, stringPosition, stringColor);
}

Make a basic running sprite effect

I'm building my very first game with XNA and i'm trying to get my sprite to run.
Everything is working fine for the first sprite.
E.g : if I go right(D) my sprite is looking right , if I go left(A) my sprite is looking left and if I don't touch anything my sprite is the default one.
Now what I want to do is if the sprite goes Right, i want to alternatively change sprites (left leg, right leg, left leg etc..) xCurrent is the current sprite drawn xRunRight is the first running Sprite and xRunRight1 is the one that have to exchange with xRunRight while running right.
This is what I have now :
protected override void Update(GameTime gameTime)
{
float timer = 0f;
float interval = 50f;
bool frame1 = false ;
bool frame2 = false;
bool running = false;
KeyboardState FaKeyboard = Keyboard.GetState();
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
if ((FaKeyboard.IsKeyUp(Keys.A)) || (FaKeyboard.IsKeyUp(Keys.D)))
{
xCurrent = xDefault;
}
if (FaKeyboard.IsKeyDown(Keys.D))
{
timer += (float)gameTime.ElapsedGameTime.TotalMilliseconds;
if (timer > interval)
{
if (frame1)
{
xCurrent = xRunRight;
frame1 = false;
}
else
{
xCurrent = xRunRight1;
frame1 = true;
}
}
xPosition += xDeplacement;
}
Any ideas...? I've been stuck on this for a while..
Thanks in advance and let me know if you need any other part from the code.
You forgot to reset the timer, you could do this when you hit the timer interval condition. Also, 50ms seems a bit too small for an interval, maybe you could do like 400ms?
Other than that, looks good, it will do what you want.
Alternatively, you could look into making animated sprites for walking. What you do is make an image with the walking animation sprites one next to the other, of the same size. You draw only a portion of this image (one sprite) and move through them based on time.
Here is a quick code for what could be an animated texture:
class AnimatedTexture2D : Texture2D
{
int _numberOfImages;
int _currentImage = 0;
int _timeInterval;
int _spriteWidth;
public Rectangle DrawFromRectangle
{
get
{
return new Rectangle(_currentImage * _spriteWidth, 0, _spriteWidth, this.Height);
}
}
public AnimatedTexture2D(Texture2D entireImage, int spriteWidth, int numberOfImages, int timeInterval)
: base(entireImage.GraphicsDevice, entireImage.Width, entireImage.Height)
{
_numberOfImages = numberOfImages;
_timeInterval = timeInterval;
_spriteWidth = spriteWidth;
Color[] data = new Color[entireImage.Width * entireImage.Height];
entireImage.GetData<Color>(0, new Rectangle(0, 0, entireImage.Width, entireImage.Height), data, 0, entireImage.Width * entireImage.Height);
this.SetData<Color>(data);
}
public void Animate(GameTime gameTime)
{
int totalImageTime = _timeInterval * _numberOfImages;
int currentPoint = (int)gameTime.TotalGameTime.TotalMilliseconds % totalImageTime;
_currentImage = currentPoint / _timeInterval;
}
}
Usage is fairly simple:
1) declare it somewhere:
AnimatedTexture2D animatedTexture;
2) initiate it with your texture (i had a 2560x64 sequence of 40 64*64 images), where individual images are placed next to each other horizontally:
animatedTexture = new AnimatedTexture2D(Content.Load<Texture2D>(#"Textures\Loading"), 64, 40, 20);
3) in your Update() method, call:
animatedTexture.Animate(gameTime);
4) in your Draw() method, call:
SpriteBatch.Draw(animatedTexture, new Rectangle(20, 20, 64, 64), animatedTexture.DrawFromRectangle, Color.White);
Don't forget the DrawFromRectangle in part 4! (notice that the destination rectangle uses the declared individual part width, not the entire texture width which is in my test 2560 pixels)
Now, in your code you could forget the interval part, and the gametime part, and you could just use this instead of the default one!
Also, if you don't like my timing code (its ultra simple but lacks a way to reset the animation) change it so you have an elapsed time variable, and add to it like you do in your own code, and use that to change _currentImage. You could even make that variable public so you can use it to reset the animation (or set it to a specified point).
Of course, you could also make the default one an animated texture with only one frame so you can use the same code everywhere. Hope this helps!
You need to keep the last time Update(..) was called, and the interval should be... well.. an interval, that is, the difference between ElapsedGameTime and last call to update ElapsedGameTime.
Do it with a new member of your class (LastElapsedGameTimeUpdateCalled) or a static member of your sub.
If you know that every animation is going to have the same number of frames you could keep 3 variables per sprite (encapsulate in class for best results).
BaseFrame is an integer to hold a global animation number.
SubFrame is an offset in to the animation that holds which frame you are currently on.
FrameAccumulator to hold timing information.
Each time update is called, add the number of ms since the last update to the accumulator. Once the accumulator goes above your animation timing, increment SubFrame, and reset the accumulator. Check to see if subFrame is greater than the number of frames for each animation, and if it is, set it back to 0. You can get the real frame index from this by adding BaseFrame + Subframe. When you need to display a different animation just change the BaseFrame.
Lets say that each animation has 3 frames, and you have 2 total animations. You would have 6 total frames. RunLeft would be BaseFrame 0, and RunRight would be BaseFrame 3. That should easily give you the frame number to draw.

Categories

Resources