Moving and scaling an object according to hand position - c#

I need some help with my college project. I have a cylinder and need it to act as a coil. For example, if I touched the cylinder's surface it's height will decrease (scaled in the y direction) as if pressing on a coil then when I remove my hand it returns back to its original size.
This is what I reached till now but I still have some problems that I can't solve.
public class Deformation : MonoBehaviour
{
Vector3 tempPos;
private void InteractionManager_SourceUpdated(InteractionSourceUpdatedEventArgs hand)
{
if (hand.state.source.kind == InteractionSourceKind.Hand)
{
Vector3 handPosition;
hand.state.sourcePose.TryGetPosition(out handPosition);
float negXRange = transform.position.x - transform.localScale.x;
float posXRange = transform.position.x + transform.localScale.x;
float negYRange = transform.position.y - (transform.localScale.y / 2);
float posYRange = transform.position.y + (transform.localScale.y / 2);
float negZRange = transform.position.z - transform.localScale.z;
float posZRange = transform.position.z + transform.localScale.z;
float handX = handPosition.x;
float handY = handPosition.y;
float handZ = handPosition.z;
if ((negXRange <= handX) && (handX <= posXRange) && (negYRange <= handY) && (handY <= posYRange) && (negZRange <= handZ) && (handZ <= posZRange))
{
tempPos.y = handPosition.y;
transform.localScale = tempPos;
}
else
{
tempPos.y = 0.3f;
transform.localScale = tempPos;
}
}
}
// Use this for initialization
void Start()
{
tempPos = transform.localScale;
InteractionManager.InteractionSourceUpdated += InteractionManager_SourceUpdated;
}
I attached two scripts to my object (cylinder) the TapToPlace script from the HoloToolKit and the deformation script stated above. The problem is when I deploy to my HoloLens to test, when I place the cylinder first to the needed place then try to deform it after that, it is placed but not deformed. If I tried it the other way around both work. Any ideas why does the deformation script does not work after the TapToPlace one?
The cylinder when viewed by my HoloLens is somehow transparent. I mean that I can see my hand through it. I need it to be more solid.
I wonder if there is something like a delay that I can use because when I use the deformation script stated above the cylinder is scaled to my hand position then scaled back to its default size very fast and appears as if blinking.
At first I place the cylinder on a setup (something as a table for example) then I begin to deform it. When I commented the else part in the deformation script stated above, it was scaled and left stable without returning to the original size. It is scaled symmetrically so its height is decreased from up and down resulting in the base of the cylinder becomes away from the table. I need the base of the cylinder to be always stable and touching the table under it.
Note: I am using Unity 2017.3.1f1 (64-bit) - HoloToolkit-Unity-2017.2.1.3
Thank you in advance.

1) Did you see the MRTK 2017.2.1.4 release? It has some useful features such as two handed resizing/scaling of objects. The BoundingBox code in the new MRTK release does moving and resizing in one component, it might be a better base to start from than the TapToPlace, or at least show how the two types of transform can work together.
2) What colour is your object? Hololens will render black as transparent, so try making the object bright white for testing. Also, just double check the brightness is turned up to full (the LHS buttons on the hololens). Finally, check your shader is the MRTK Standard shader. (again, the 2017.2.1.4 release has new shader code you might want to try.) . In a room without direct sunlight it should pretty much cover up your hand.
4) I'm not sure I follow completely, but the pivot point could be important here. If it is centred in the middle of the coil (as I'd imagine it is) then when you deform the coil down it will still stay centered at that central pivot point.
If you instead set the pivot point to the bottom of the coil, touching the table, you can scale and that point stays on the table and the top does all the moving.

Related

Make GameObject “attach” properly?

This script makes a cube "stick" to whatever it collided with. The problem is that when it's going at relatively high or medium speeds (or when the device itself is slow), the cube tends to "get a bit inside" what it collided with and then stick to it. What changes do I have to make to fix this?
In order for this script to work, one GameObject must have bool _sticksToObjects = true; and the other bool _sticksToObjects = false;
I have tried turning the Rigidbody's Collision Detection mode to either Continuous or Continuous Dynamic
I think my script depends on frame rate. That may be where the problem lies.
Normal "Attach":
Abnormal "Attach":
Rigidbody _rigidBody;
Transform _meshTransform;
bool _sticksToObjects = true;
public Transform _stuckTo = null;
protected Vector3 _offset = Vector3.zero;
void Awake()
{
GameObject CubeMesh = GameObject.FindWithTag ("CubeMesh");
GameObject Cube = GameObject.FindWithTag ("Cube");
_rigidBody = Cube.GetComponent<Rigidbody> ();
_meshTransform = CubeMesh.GetComponent<Transform> ();
}
void Update()
{
if (_stuckTo != null)
{
transform.position = _stuckTo.position - _offset;
}
}
void OnCollisionEnter(Collision collision)
{
if (!_sticksToObjects) {
return;
}
_rigidBody.isKinematic = true;
// Get the approximate collision point and normal, as there
// may be multipled collision points
Vector3 contactPoint = Vector3.zero;
Vector3 contactNormal = Vector3.zero;
for (int i = 0; i < collision.contacts.Length; i++) {
contactPoint += collision.contacts [i].point;
contactNormal += collision.contacts [i].normal;
}
// Get the final, approximate, point and normal of collision
contactPoint /= collision.contacts.Length;
contactNormal /= collision.contacts.Length;
// Move object to the collision point
// This acts as setting the pivot point of the cube mesh to the collision point
transform.position = contactPoint;
// Adjust the local position of the cube so it is flush with the pivot point
Vector3 meshLocalPosition = Vector3.zero;
// Move the child so the side is at the collision point.
// A x local position of 0 means the child is centered on the parent,
// a value of 0.5 means it's to the right, and a value of -0.5 means it to the left
meshLocalPosition.x = (0.5f * contactNormal.x);
_meshTransform.localPosition = meshLocalPosition;
if (_stuckTo == null || _stuckTo != collision.gameObject.transform) {
_offset = collision.gameObject.transform.position - transform.position;
}
_stuckTo = collision.gameObject.transform;
}
Here are some screenshots of the Unity editor:
This is a well-known category of problem in game engineering and you'll be pleased to know the solution is relatively simple. You'll be pleased to hear there are similar, but much more complicated, problems that are actually solved in the same way. I'll try to explain.
Now here's the thing. It's quite often that the following question comes up...
So I'm working on GTA. I have a humanoid, H, running around. She approaches vehicle V. She opens the door and gets in and drives off. After that everything goes to hell in Mecanim and all the code stops working. What to do?
Surprisingly, the way that is done in games is:
Surprisingly: you actually swap to totally different models at that point!!!!!
You have H and V in the game. But then you have an animation (say) for H climbing in to V. But then, you literally destroy the game objects of H and V, and you Instantiate (or just awake) a new, totally different, game object, which is D ("a car being driven around by a lady").
(If you think about it, you can see that when you do this, you carefully adjust all the stuff in D, so that it matches what was "just then happening" in the frame, in relation to both H and V. So for example, literally, you copy the transform, twist etc of the car V, to the new car-inside-D, if lady H has the SmearedMakeupEffect, you put the same SmearedMakeupEffect on the lady-within-D, you position all the bones identically, and so on.)
Another simple example of this is, you often get people asking, "my character C gets killed and I want it to become a ragdoll, how to?" In fact you just swap to a totally new game object you have all set up for that passage of the game. Indeed, if you have a character A ("Arnie") in a game, it's normal that you have 4 or 5 "different As" sitting offside the stage, so, there's "ragdoll A", "A who can dance" "A with weapon". And indeed many of these are combos, you know "A on the horse" "A in the car" and so on.
So interestingly, the "real" solution here is,
once they become a new connected thing, destroy them both and swap to a new game object altogether!
if you have made games "until you are blue in the face" from making games, this is just what you would do as a matter of course. Even though its' a simple situation, it's just easier in the long run. After all, consider all the stuff you have to do when this happens:
make hitting object child of the other
turn off physics on the child
change the way your physics works for the whole thing
turn off or change the collider on the hitting object, perhaps making it part of the overall object
you'll likely have some sort of new "separation" physics where it can be knocked-off - you'd have to turn all that on
likely change minor issues like sound effects, colors etc
As you can see it's a huge chore doing all this stuff, and indeed it's one of those things it's just "easier to do properly" and change to a new model.
All that being said, I know you want a Quick Script Solution you can Paste In :) Here it is...
Step 0, You'll create "YourScript" which goes on the "main" cube. it will "catch" another cube moving around.
YourScript will look basically like this ...
[System.NonSerialized] public bool isConnectedNow;
void OnCollisionEnter(Collision collision)
GameObject theThingWeCaught = collision.gameObject
Debug.Log("We caught this thing .. " + theThingWeCaught.name)
// make it a child of us......
theThingWeCaught.transform.parent = transform
theThingWeCaught ... set kinematic
theThingWeCaught ... probably disable the rigidbody
theThingWeCaught ... probably disable the collider
isConnectedNow = true;
That's really all you have to do.
Step 1, YOUR script must have a public bool like this
[System.NonSerialized] public bool isConnectedNow;
Step 2, Here's MyScript which goes on the hitting cube, first we'll unit-test that your isConnectedNow bool is working
public Class MyScript:MonoBehaviour // attach to the "child" cube
{
public float correctXDistance;
public float correctYDistance;
public Transform bigCube;
public YourScript yourScript;
void Update()
{
string message = yourScript.isConnectedNow ? "free" : "stuck";
Debug.Log("I am " + message);
}
}
attach, debug, and run. Make the little cube stick and unstick from the big cube .. Watch the console. it works? So add this to MyScript
private void DistanceCorrectionX()
{
float xDistance = bigCube.position.x - transform.position.x;
float xSign = Mathf.Sign(xDistance);
float xDelta = Mathf.Abs(xDistance);
float closenessPercentage = (xDelta/correctXDistance)*100f;
if ( closenessPercentage<90f || closenessPercentage>110f)
{
// they are not close enough to quantize on this axis
// this comes in to play when you have multiple axes
return; // do nothing.
}
float xShouldBe = bigCube.position.x + xSign * correctXDistance;
Vector3 p = transform;
p.x = xShouldBe; // be careful it's .y, .z etc for other axes
transform.position = p;
}
for now call that in Update() in MyScript like this
void Update()
{
Debug.Log("I am " yourScript.isConnectedNow ? "free" : "stuck");
if (yourScript.isConnectedNow) DistanceCorrectionX();
}
Now actually Play and make it stick. Now, since it's running in Update simply while Play look at the Inspector for MyScript and adjust the value of correctXDistance to get the exact look you want. When yo have decided on a value, unPlay and put that in as the final value you wish.
Next, in DistanceCorrectionX simply duplicate all the code and do it again for the Y axis DistanceCorrectionX. If you also do Z, do that.
Finally. Note you will have a lot of messy code, like this...
void Update()
{
// handle all the DistanceCorrectionX etc as seen above.
if (yourScript.isConnectedNow)
{
.. turn off the collider on me
}
else
{
.. turn on the collider on me
}
}
and so on, there's "many little things" you'll need to do.
Don't forget also, overwhelmingly you may want to make the hitting object a child of the big object, depending on your situation. (Then of course they would move around together as a unit.)
Note that in the positioning code above I just showed it as position, not local position, for pedagogic clarity. If you want to do them flinging around, and spinning and so on, you'd make the hitting object a child of the other and you would use localPosition in the same way. Enjoy.
One possible way that comes to my mind is:
Inside of the "collision enter" check the distance between these objects and move the one that should stick to the other one a bit away.
As you see in the picture the distance between A and B should be equal to the sum of the widths divided by 2 (with a small threshold of course).
If the distance is less than the sum of the widths / 2 then you have an abnormal "attach" and you have to move one of the objects away. Its not really difficult to accomplish that.

Jump animation using animation class

I need a little help in my little 2D game I want to create in XNA. I had almost no knowledge of programming before I got interested in XNA and C#, so maybe my problem is simple, but I just can't figure it out.
So basically, I have a base class, and I created an additional class Animation for animating sprites. I implemented some methods so that when the player presses "right" it would change the animation's current texture and increment X by a number of xf; anyway, the main idea is that I'm using just one instance of my class (basically, one object of type animation which changes its texture and properties based on what key is pressed).
So, I had no problems making it run right or left. Works out pretty well. The big problem started when I wanted to implement the jump sprite. So I created the 6 frames necessary for the sprite, but to animate it I have virtually no idea how to do it.
The only thing it does right now is to loop through the frames of the sprite, but the position (both .X and .Y) remain the same. The thing is, I have a Vector2 position which holds the animation's current position, and it's fine with running because I simply increment it. However, when it comes to jumping, I want it to increment .X, but the .Y should be decremented (thus going up) until frame number 3; after frame number 3, until the last frame, I want the .Y position to go down (thus fall) with the corresponding animations (erm, frames).
So, basically, I don't know how to modify the .X and .Y so that it would display the frames that I need in the time I need. I don't know if you really understood what I'm trying to say; basically when I press the "up" key, it loops through the frames but the position remains the same.
My idea was to use a reference to the actual Vector2 position which holds the animation's current position and pass it to the method in the other Animation.cs class, namely the PlayAnimJump, and modify the position after each frame and return it to the actual Game1.cs by reference. Even if I would do that (though I fail to see what good it would be), it wouldn't be updating the position as it should. So, any ideas?
Here is the code for the PlayAnimJump method from the Animation class:
public void PlayAnimJump(GameTime gameTime)
{
elapsed += (float)gameTime.ElapsedGameTime.Seconds;
sourceRect = new Rectangle(currentFrame * frameWidth, 0, frameWidth, frameHeight);
currentFrame = 0;
if (elapsed >= frameTime)
{
if (currentFrame <=3)
{
if (looping)
{
currentFrame++;
}
}
else if (currentFrame > 3)
{
currentFrame++;
}
elapsed = 0;
}
}
The default constructor for that class:
public Animation(ContentManager Content,string asset,float frameSpeed, int numbOfFrames, bool looping,Vector2 positionIT)
{
this.assetName = asset;
this.frameTime = frameSpeed;
this.numbOfFrames = numbOfFrames;
this.looping = looping;
this.animation = Content.Load<Texture2D>(asset);
frameWidth=(animation.Width / numbOfFrames);
frameHeight=animation.Height;
position = positionIT;
}
Here is the code (from the main) when the up key is pressed:
else if (up)
{
check = animation1.GetAsset();
if (check == "eright")
{
animation1.SetFrameSpeed(0.8f);
animation1.SetNumbOfFrames(6);
animation1.ChangeTexture(Content, "SarimCumVreJiorjica");
animation1.PlayAnimJump(gameTime);
/*position1.x +=2f;
position1.Y -=2f;
*/
}
So, I'm not sure how, but I'm supposed to change position1 according to the frame that's displayed by the animation in that second. Am I missing something?
If your animation class had a reference to the object that you wanted to move (i.e the object holding the position field) then you could modify it within the animation class, within the PlayAnimJump method.
Or, to reduce coupling, you could just have PlayAnimJump return a variable indicating how far into the jump you are (maybe a percentage of the jump, from 0 to 1). Then, you could use the percentage outside to set the objects position. So, if the jump is halfway done, the return value would be 0.5f, which you could use in an equation to determine the players y position. An example equation would be:
float percent = animation1.PlayAnimJump(gameTime);
float y = Math.Sin(percent * Math.PI) * maxJumpHeight;
player.positon.y = y;
This uses a sine wave to determine the players height throughout the jump animation. You would just need to write the code that determines the percentage of the way through the jump (currentFrame) in the PlayAnimJump method and return it.
Formula of the frуefall for Y coordinate is
y = g * t ^ 2 / 2 + v0 * t + y0
Characters jump from height y0 vith start velocity v0 by Y axis and gravity gradually slows down and starts to fall.
Calculate deltaY using following formula
deltaY = g * t ^ 2 / 2 + v0 * t
First show the frame on which the character is pushed off the ground, then the frame on which it rises until it reaches the peak of the jump. Once the sign change deltaY from + to - show how the character change pose for fall. Something like that.

Calculating/Predicting a way

I'm just starting with physics, so I'm not always sure about what I'm doing. It's a 2D project but I'm using 3D physical objects like SphereCollider etc..
What I have:
Objects floating in space and affecting each other through gravity:
protected virtual IEnumerator OnTriggerStay(Collider other) {
yield return new WaitForFixedUpdate();
if(other.attachedRigidbody) {
Vector3 offsetVector = this.transform.position - other.transform.position;
float distance = offsetVector.magnitude;
float gravityForce = (other.rigidbody.mass * mass) / Mathf.Pow(distance, 2);
// Clamp gravity.
if(gravityForce > 1.0F) {
gravityForce = 1.0F;
}
other.attachedRigidbody.constantForce.force = offsetVector.normalized * gravityForce;
}
}
There are controllable objects on which the player can click and drag a line away from the object in order to give it a force (shoot) in the opposite direction.
What I want to achieve:
The player should see a rough prediction of the way while aiming. That means that the way-prediction needs to take in account the current velocity, the force which would be applied when the player release the mouse button and the gravity of the surrounding objects.
What I have tried so far:
For testing purposes I just save the computed/predicted positions in an array and draw those positions in OnDrawGizmos().
I wrote a method which returns the gravity influence for a certain position called computeGravityForPosition(Vector3 position).
And thats how I try to calculate the positions:
private void drawWayPrediction() {
Vector3 pos = this.transform.position;
// The offsetVector for the shooting action.
Vector3 forceVector = pos - Camera.main.ScreenToWorldPoint(Input.mousePosition);
forceVector.z = 0.0F;
// The predicted momentum scaled up to increase the strength.
Vector3 force = (forceVector.normalized * forceVector.magnitude);
// 1. I guess that this is wrong, but don't know how to do it properly.
momentum = this.rigidbody.velocity + force;
for(int i = 0; i < predictionPoints.Length; i++) {
float t = i * Time.fixedDeltaTime;
momentum += computeGravityForPosition(pos);
pos += momentum * t * t;
predictionPoints[i] = pos;
}
}
At the beginning, when the objects just slowly approaching each other it looks okay. After the first shot, the prediction is completely wrong. I guess it is because of 1. in the code. Just adding the force to the velocity is probably horrible wrong.
Thank you very much for your time.
EDIT:
I removed seemingly unnessecary parts.
I still think that the main problem lays in 1. in the code. I just don't know how to mix up the current movement of the object (from which I only have the current velocity as far as I know the physics engine of unity) with the new created force:
Vector3 forceVector = pos - Camera.main.ScreenToWorldPoint(Input.mousePosition);
Vector3 force = (forceVector.normalized * forceVector.magnitude);
So if you are using a new version of unity probably above 2018, you can use the nice method
Physics.Simulate(dt); // delta time, dt, is the amount of time to simulate.
https://docs.unity3d.com/ScriptReference/Physics.Simulate.html
https://docs.unity3d.com/2018.3/Documentation/ScriptReference/PhysicsScene.Simulate.html
By using this function you can manually advance the simulation.
This method should be applied to a different physics scene.
Therefore I suggest that when you click you will simulate a few physics steps (the more you will simulate the more accurate indication the player will get),
with every step you store the position of the object and when you are done simulating draw a line between all the points.
In my opinion, it should run quite fast if done correctly.
The code should look something like this:
public PhysicsScene physicsScene;
GameObject actualBall;
GameObject simulatedBall;
OnClick() {
simulatedBall.SetPosition(actualBall.transform.position);
if (!physicsScene.IsValid())
return; // do nothing if the physics Scene is not valid.
for (int i=0; i < 10; i++) {
physicsScene.Simulate(Time.fixedDeltaTime);
// store the position.
myPoints.append(simulatedBall.rb.position);
}
// draw a line from the stored points.
}
In addition there is this video that I hope will help, good luck
https://www.youtube.com/watch?v=GLu1T5Y2SSc
I hope I answered your question and if not tell me :)
Disclaimer : Unfortunately I suck at math so can't provide any code for the calculations.
Now that the legal stuff is out of the way :)
In my opinion you are looking at this all wrong. What you need is to calculate the curve (path of the objects trajectory) and then simply plot the curve in OnDrawGizmos with a line renderer.
You don't need to simulate the behaviour of the object. Not only is this a LOT faster but it's also simpler in terms of TimeScale shenanigans. By changing the TimeScale you are also affecting the TimeScale of your trajectory simulation which will most likely look and feel weird.
By doing a basic trajectory calculation you will not have this issue.
PS: This link might help.

Animated Title (Like Terraria's Game Title)

What i want to do is make a title like Terraria just the rocking back and forth side of it not the graphics side i know that its just a .png rocking back and forth but could anyone help me and other people who read this and what to know how to do it?
So what i would like is to learn how to make a rocking back and forth image like the title displayed in Terraria?
Something like this for the people who don't know what Terraria is.
http://www.youtube.com/watch?v=3K8PMG42l3M
It appears that the logo is rotating and changing its size over non-equal intervals of time.
First, you need to get familiar with this method:
SpriteBatch.Draw Method (Texture2D, Vector2, Nullable, Color, Single, Vector2, Single, SpriteEffects, Single)
The parameters are:
Texture2D texture, // texture of your logo
Vector2 position, // where to draw
Nullable<Rectangle> sourceRectangle, // null
Color color, // Color.White
float rotation, // you will be changing this
Vector2 origin, // and this
float scale, // also this
SpriteEffects effects, // SpriteEffects.None
float layerDepth // 0
Use these variables:
float rotation = 0,
rotationSpeed = 0.002f, // this is how much rotation will change each frame
maximumAngle = 0.1f,
minimumAngle = -0.1f,
rotationDirection = 1,
scale = 1f, // 1 means 100%, 0.95f = 95%
scaleChange = 0.005f, // this may seem not much, but it's enough
maxScale = 1.1f,
minScale = 0.9f,
scaleDirection = 1;
Just put DrawLogo(); in your main Draw() method.
void DrawLogo()
{
// these two very similar pieces of code will control scale and rotation
if (rotationDirection > 0)
{
if (rotation < maximumAngle)
rotation += rotationSpeed;
else
rotationDirection = -rotationDirection;
}
else
if (rotation > minimumAngle)
rotation -= rotationSpeed;
else
rotationDirection = -rotationDirection;
if (scaleDirection > 0)
{
if (scale < maxScale)
scale += scaleChange;
else
scaleDirection = -scaleDirection;
}
else
if (scale > minScale)
scale -= scaleChange;
else
scaleDirection = -scaleDirection;
Texture2d t2d = logoTexture;
spriteBatch.Draw(t2d,
centerScreen, // change this to `new Vector2(123, 456)`
null, // null means draw entire texture
Color.White, // no tinting
rotation, // the rotation we calculate above
new Vector2(t2d.Width / 2, t2d.Height / 2),
// this sets rotation point to the center of the texture
scale, // the scale we calculate above
SpriteEffects.None, // you can mirror your texture with this
0); // I usually leave it zero :p
}
This is tested and works just fine :)
You mean the effect we can see at about 1:16 (and probably also at other times), when you choose stuff in the menus?
Concept
As far as I can see, you can do this with simple rotations and scaling. So, if you do not want to make an animated gif (which you suppose it is), you can just do it inside your XNA code. Take a png or gif with alpha-channel (so that the non-text is transparent).
Then, when you draw it on the screen with spriteBatch.draw() you can choose one of the overloads that support scaling and rotation.
Then you have to set:
the rotation you want to have (which will be a rotation over time)
the origin (to the center of the image)
the scale (which will be scaling over time)
As the clock is sent to the update() method as far as I remember XNA, we will have to update the rotation and scale of the image there. We need the clock, because we cannot just set rotaton = 10° and XNA will handle everything for us. We have to calculate the current rotation in each time step ourselves. E.g. if a full rotation shall endure 10 seconds and 5 seconds have passed, then you know you have a half rotation. So we would tell XNA: Set our rotation to 180° now, and in the next time step, we might tell: Set our rotation to 190° now.
The basic concept is:
Calculate how much part of rotation/scale we have done in the current time step
Tell XNA to adjust this rotation/scale in this time step
Iterate these two steps again and again
Implementation
I think the best thing to do here, is using a sin() or cos() function for the scaling and rotation. The good things about them:
they have positive and negative values as well (so we can easily rotate in both directions)
they are smooth, meaning your rotation and scaling will not look too abrupt at the end of the rotation/scaling
I hope my maths is correct here. I will explain everything, so others can correct me if something is wrong. Or also you can find out, if something is wrong. We will use a sin() here, because it starts at 0, which in our case means that nothing should happen. That’s what we want: We want to begin at a situation where nothing happens.
Now, sin() has a cycle time of 2*PI. Of course, we do not want a scaling to last 2*PI, but rather something like 1000 milliseconds. We cannot change the definition of Math.Sin() in C#, but we can change the value we throw inside. So when we mean 1000 milliseconds, we will give Math.Sin() 2PI and when we mean 500 milliseconds, we give it PI.
We would define these member variables:
// define some variables for rotation and scale speed, in milliseconds
int fullRotationTime = 1000; // max rotation will be reached after 1 second
float maxRotationAngle = MathHelper.ToRadians(10); // we will rotate by 10 degree up and down
int rotationTimePassed = 0;
float currentRotationAngle = 0;
int fullScaleTime = 1000; // max scale will be reached after 1 second
float maxScaleSize = 1.2f; // we will scale to 20% larger max
int scaleTimePassed = 0;
float currentScaleFactor = 1.0;
And in the Update() method, we calculate how much of our rotation we already have done.
protected virtual void Update(GameTime gameTime)
{
int milliseconds = gameTime.ElapsedGameTime.TotalMilliseconds;
// these are the milliseconds in the current rotation
rotationTimePassed += milliseconds;
scaleTimePassed += milliseconds;
if (rotationTimePassed >= fullRotationTime)
rotationTimePassed %= fullRotationTime;
if (scaleTimePassed >= fullScaleTime)
scaleTimePassed %= fullScaleTime;
float rotationTimeAdjustedToTwoPi = ((float)rotationTimePassed)/fullRotationTime * 2* Math.PI);
currentRotationAngle = maxRotationAngle * Math.Sin(rotationTimeAdjustedToTwoPi);
// we do not want the scaling to be negative, thus we add 1 to the whole and
// divide by 2. Then the maximum will be 1 and the minimum 0
float scaleTimeAdjustedToTwoPi = ((float)scaleTimePassed)/fullScaleTime * 2* Math.PI);
currentScaleFactor = maxScaleSize * (Math.Sin(scaleTimeAdjustedToTwoPi) + 1)/2;
}
Then, in the Draw() method we can take the values calculated before and display our rotated and scaled image.
protected virtual void Draw()
{
spriteBatch.Begin();
spriteBatch.Draw(texture,
new Vector2(50, 50),
null,
Color.White,
currentRotationAngle,
new Vector2(texture.width/2, texture.height/2),
currentScaleFactor,
SpriteEffects.None,
0
);
spriteBatch.End();
}
It’s not tested, so there might even be syntax errors, but I at least the basic idea should be correct and I think the important thing is that you understand how it can be done conceptually.
Variable time steps
It’s easy to integrate the variable time steps user1306322 has mentioned into the code above. We had these if-conditions where we checked if the current time-slice is over, like this: if (rotationTimePassed >= fullRotationTime).
Now it we want to make the time-slices variable length, just adjust a new time-slice based on a random number here. Like this:
var rand = new Random();
if (rotationTimePassed >= fullRotationTime)
{
rotationTimePassed %= fullRotationTime;
// next rotation might take between 0.5 and 2.5 seconds
fullRotationTime = rand.next(500, 2500);
}

OnCollision event handler problems in C# XNA with Farseer Physics

I have this working ok(ish) in my game at the moment, but i'm not fantastic at maths. When two primatives collide, I want them to smash up into tiny bits if the force applied to a primative was over a set threshold. My collision event handler at present looks like this.
public bool Collision(Fixture fixtureA, Fixture fixtureB, Manifold manifold)
{
Vector2 position = manifold.LocalNormal;
float angle = (float)Math.Atan2(position.Y, position.X);
Vector2 force = Vector2.Zero;
if (angle < 0)
force = new Vector2((float)(Math.Cos(angle) * fixtureA.Body.LinearVelocity.X), (float)Math.Sin(MathHelper.TwoPi + angle) * fixtureA.Body.LinearVelocity.Y);
else
force = new Vector2((float)(Math.Cos(angle) * fixtureA.Body.LinearVelocity.X), (float)Math.Sin(MathHelper.TwoPi - angle) * fixtureA.Body.LinearVelocity.Y);
double XForce = Math.Sqrt(force.X * force.X);
double YForce = Math.Sqrt(force.Y * force.Y);
double totalForce = XForce + YForce;
if ((Breakable) && (totalForce > BreakForce))
{
Breakable = false;
Active = false;
BreakUp(fixtureA, fixtureB);
}
return true;
}
I put that in a LONG time ago when I was just playing around. This causes a bit of a problem in certain situations. For example, if a primative is stationary on the floor and another primative falls onto it from a decent height, almost always, the falling box blows up and the resting box survives. Also if two boxes are falling side by side and give each other the tinyest of touches, then both boxes blow up mid air. Hmmmmm, not really perfect that. Does anyone have any idea how to improve my collision handler? Thanks in advance.
Ok, so my other answer is viable. But I've looked at Farseer 3.0 (the current SVN version) more closely and found that it already implements almost exactly what you are trying to do.
Look for "BreakableBody.cs". You may be able to directly use that - but otherwise you could just copy out the functionality you want.
Specifically: Instead of attaching a function to your fixture's OnCollision you want to attach one to PostSolve. It takes a ContactConstraint which you can dive into and find the impulses from the collision.
This is the implementation of that function used by BreakableBody:
private void PostSolve(ContactConstraint contactConstraint)
{
if (!Broken)
{
float maxImpulse = 0.0f;
for (int i = 0; i < contactConstraint.manifold.PointCount; ++i)
{
maxImpulse = Math.Max(maxImpulse,
contactConstraint.manifold.Points[0].NormalImpulse);
maxImpulse = Math.Max(maxImpulse,
contactConstraint.manifold.Points[1].NormalImpulse);
}
if (maxImpulse > Strength)
{
// Flag the body for breaking.
_break = true;
}
}
}
Apparently the impulse data in the manifolds is only set correctly in PostSolve, not in OnCollision.
(While this is currently the accepted answer - I would direct anyone to my other answer for a potentially superior approach.)
Your calculation of impact force is completely wrong. You need to get the relative velocity at the contact point(s) - you're getting something quite strange...
Your code looks like it's using Farseer 3.0 (you should specify, because that's more of a fork of Box2DX than Farseer 2.1). What I did in Farseer 2.1 (where you've got ContactList contacts instead of a Manifold) to get the impact velocity was:
foreach(Contact contact in contacts)
{
Vector2 position = contact.Position;
Vector2 v0;
me.Body.GetVelocityAtWorldPoint(ref position, out v0);
Vector2 v1 = new Vector2();
if(!hit.Body.IsStatic)
hit.Body.GetVelocityAtWorldPoint(ref position, out v1);
v0 -= v1;
float hitVelocity = v0.Length();
// To then get the force, you need the mass of the two objects
}
From a brief look at the Farseer 3.0 source, it seems that Manifold has a member:
public FixedArray2<ManifoldPoint> Points;
And both Manifold and ManifoldPoint have members:
public Vector2 LocalPoint;
It should be fairly simple to modify my Farseer 2.1 code to use those instead.
Also: I recommend simply marking the two objects as needing to break, and then actually breaking them after your physics update finishes running (rather than in the collision handler).
I haven't used XNA, but as a physics problem, why not just subtract the linear velocity of A from the linear velocity of B, and get the 'force' as the square of the resulting vector (sum the squares of the components)? That should harmonize with the kinetic energy involved according to `E=(mv^2)/2', for a very simple physical model, even if we are ignoring the masses (or, for that matter, elasticity or the distinction between energy and momentum). If the objects are moving in the same general direction at the same speed, you get a small value; if one is approaching (or, of course, departing!) at high speed, you get a large value.

Categories