Animated Title (Like Terraria's Game Title) - c#

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);
}

Related

Calculating changing Mathf.Clamp() values for object of changing size

I've been wracking my brain over this problem for a while. I have a controllable object (only moveable up and down) that I have clamped on the top and bottom. However, every time a certain trigger is activated, the object shrinks 10% until it is 90% of the original size. The problem comes in when I shrink it. When it shrinks, the min and max values of the clamp don't change at all and this results in the object clamping to soon on the min and max. They need to decrease and increase respectively so that it clamps in the exact same place. How would one do this? I've tried so many different methods but to no avail...
This is the way I shrink my player:
playerWidth /= 1.1f;
playerHeight /= 1.1f;
Vector3 scale = new Vector3(playerWidth, playerHeight, 1f);
playerOneShrink.transform.localScale = scale;
Many Thanks!
EDIT 1:
Let me give an example using a game that might help get my point across better. Basically think flappy bird, but the player uses the up and down arrows to move and not space-bar. When the bird dies, the bird would reset but become smaller. It seems that because the bird is smaller it now moves 'less' up and down, and doesn't touch the top and bottom of the screen anymore. I want the bird to still be touching the top and bottom of the screen.
EDIT 2:
So apparently my first edit didn't clarify as much as I thought so I'm going to show my code for a test scene I'm trying this all in. I haven't shown the changing of minValue and maxValue because that's what I'm struggling with...
private static Rigidbody2D playerOneRB;
GameObject player1;
float verticalMovement = 0;
public static float playerHeight = 0.3417f;
public static float playerWidth = 0.05400001f;
public float minValue = 0;
public float maxValue = 10;
public static float p1VerticalSpeed = 3;
public GameObject playerOneShrink;
// Start is called before the first frame update
void Start()
{
playerOneRB = GameObject.FindGameObjectWithTag("P1 Normal").GetComponent<Rigidbody2D>();
player1= GameObject.Find("P1 Normal");
}
// Update is called once per frame
void Update()
{
verticalMovement = Input.GetAxis("Player 1 V"); //Looking for axis defined in our input manager
playerOneRB.velocity = new Vector3(0f, verticalMovement * p1VerticalSpeed, playerOneRB.velocity.x);
Vector3 scale1 = new Vector3(playerWidth, playerHeight, 1f);
Vector3 position = transform.position;
player1.transform.position = new Vector3(player1.transform.position.x, (Mathf.Clamp(player1.transform.position.y, minValue, maxValue)), 0f); //Restricting how far the players can move (aka, not off the screen)
if (Input.GetKeyDown(KeyCode.Alpha1))
{
playerWidth /= 1.1f;
playerHeight /= 1.1f;
scale1 = new Vector3(playerWidth, playerHeight, 1f);
paddleOneShrink.transform.localScale = scale1;
}
}
Here's a photo of what's happening:
1. Photo of position it reaches before shrinking
2. Photo of position it reaches after shrinking
I want the player in image 2 to be able to get to the same top position as in image 1 after it shrinks.
It sounds like you want to scale the object, but keep it pivoted, so if it's at the top most position, and scales, it stays at the top most position? If so, the code and answers here will help:
https://answers.unity.com/questions/14170/scaling-an-object-from-a-different-center.html
You'll of course need to add in some conditional code to check whether it's a top or bottom pivot.
Hope I understood your problem.
So let's assume the scale of the player is 1 unit and you have to clamp in y axis between 0 and 10.
so you will have to clamp the Y position of player like
Vector3 scale = new Vector3(playerWidth, playerHeight, 1f);
Vector3 position = transform.position;
position.y = mathf.clamp(0 + scale.x ,10 - scale.x ,position.y );
transform.position = position;
so if the player's scale is 1 then it will limit the y position between 1 (0+1) and 9 (10-1).
if the player's scale is 0.9 then it will be between 0.9 (0+0.9 ) and 9.1 (10-0.9)
if the scale is 3 then between 3 (0+3) and 7 (10-3)

Moving and scaling an object according to hand position

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.

Unity3d. Handles.ScaleValueHandle changes size

I have a problem with Handles.ScaleValueHandle method.
This method changes size that I pass to it.
float newRadius = Handles.ScaleValueHandle(cCollider.radius, pos, handleRot, cCollider.radius, MyCircleCap, 0);
// cCollider.radius == 0.117120504f
But in my MyCircleCap I have another size:
private void MyCircleCap(int controlID, Vector3 position, Quaternion rotation, float size)
{
// size == 0.017568076f
Handles.CircleCap(controlID, position, rotation, size);
}
So the size of the circle is wrong.
I have tried to do something like this:
sircleCapSize = cCollider.radius; // save value in separate variable
float newRadius = Handles.ScaleValueHandle(cCollider.radius, pos, handleRot, cCollider.radius, MyCircleCap, 0);
}
float sircleCapSize;
private void MyCircleCap(int controlID, Vector3 position, Quaternion rotation, float size)
{
// using saved size in variable `sircleCapSize`
// instead of size from parameters
Handles.CircleCap(controlID, position, rotation, sircleCapSize);
}
The circle now look fine, but i can't touch it by my mouse. That is because of i lied that circle are large. In fact, it small, and i can touch it only if i click on center of circle. So i tried another:
float magicNumber = 8f;
float newRadius = Handles.ScaleValueHandle(cCollider.radius, pos, handleRot, cCollider.radius * magicNumber, MyCircleCap, 0);
Now it work. Size approximately correct. But who now, maybe some day unity team will fix this bug (if it is a bug, actually) and i will have wrong size again.
Can anyone say me how to do it right?
I use Unity version:
Version 5.1.1f1 (2046fc06d4d8) Personal
Fri, 27 Mar 2015 09:26:51 GMT
Branch: 5.1/release
UPDATE 1 (adding screenshots):
I'm trying to add my own collider radius handle:
I call Handles.ScaleValueHandle( , , , cCollider.radius, MyCircleCap, ); method, and I pass an exact size of my collider (cCollider.radius) into it:
Method that calls my 'MyCircleCap'
Value that i pass to Handles.ScaleValueHandle as a size
But ScaleValueHandle changes size, so in my MyCircleCap i have another size:
Now, I am in my 'MyCircleCap' method
Value that i receive as a size
How to prevent size changing?
It seems like the ratio size/cCollider.radius = 0.15, so if you change magicNumber to 1/.15f size would be equal to cCollider.radius without the need of a temporary variable.
Does the GameObject of your cCollider Component is scalled differently than Vector3.one ?
If so reset it to Vector3.one
The constant 0.15 from Pluto is not documented because it'll only work for your project: it's just a little fix that doesn't treat the source of your problem, it just gives you the effect you want. He took your debug values and do size / cCollider.radius ~= 0.0135394121 / 0.09026274 ~= 0.15
In order to make it work do this:
float magicNumber = 1f / 0.15f;
(Sorry to use an Answer whereas it should be a comment, I need to gain reputation to do that)

Create Rect at global coordinate

My scene is 2048 x 1152, and the camera never moves. When I create a rectangle with the following:
timeBarRect = new Rect(220, 185, Screen.width / 3, Screen.height / 50);
Its position changes depending on the resolution of my game, so I can't figure out how to get it to always land where I want it on the screen. To clarify, if I set the resolution to 16:9, and change the size of the preview window, the game will resize at ratios of 16:9, but the bar will move out from where it's supposed to be.
I have two related questions:
Is it possible to place the Rect at a global coordinate? Since the screen is always 2048 x 1152, if I could just place it at a certain coordinate, it'd be perfect.
Is the Rect a UI element? When it's created, I can't find it in the hierarchy. If it's a UI element, I feel like it should be created relative to a canvas/camera, but I can't figure out a way to do that either.
Update:
I am realizing now that I was unclear about what is actually being visualized. Here is that information: Once the Rect is created, I create a texture, update the size of that texture in Update() and draw it to the Rect in OnGui():
timeTexture = new Texture2D (1, 1);
timeTexture.SetPixel(0,0, Color.green);
timeTexture.Apply();
The texture size being changed:
void Update ()
{
if (time < timerMax) {
playerCanAttack = false;
time = time + (10 * Time.deltaTime);
} else {
time = timerMax;
playerCanAttack = true;
}
The actual visualization of the Rect, which is being drawn in a different spot depending on the size of the screen:
void OnGUI(){
float ratio = time / 500;
float rectWidth = ratio * Screen.width / 1.6f;
timeBarRect.width = rectWidth;
GUI.DrawTexture (timeBarRect, timeTexture);
}
I don't know that I completely understand either of the two questions I posed, but I did discover that the way to get the rect's coordinates to match the screen no matter what resolution was not using global coordinates, but using the camera's coordinates, and placing code in Update() such that the rect's coordinates were updated:
timeBarRect.x = cam.pixelWidth / timerWidth;
timeBarRect.y = cam.pixelHeight / timerHeight;

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.

Categories