I have the following animation:
See on video: Animation Light
Note: Below image of animation control
How can I get the same result using a script? My intention is that the script has no "if".
Don't like this:
public float minIntensity;
public float maxIntensity;
public float intensityAmount;
private Light light;
void Awake(){
light = GetComponent<Light>();
}
void Update(){
if (light.intensity > maxIntensity) {
light.intensity -= intensityAmount * Time.deltaTime;
}
else if (light.intensity < minIntensity) {
light.intensity += intensityAmount * Time.deltaTime;
}
}
I wonder if there is any possibility to do this using some native function ... like: (Math.Clamp, Math.Lerp, Quaternion.Slerp) without any condition as "if" in the code.
Thank you in advance.
Well like you mentioned, you can just use a clamp:
light.intensity = Mathf.Clamp(value, minIntensity, maxIntensity)
However, despite the lack of detail on what type of animation you want, I am assuming you want to "ping pong" between the min and the max. If that is the case, we can use our friendly neighborhood sine wave for that.
public float Frequency = 10f;
public float Magnitude = 2f;
void Update() {
light.Intensity = Magnitude + Mathf.Sin(Time.timeSinceLevelLoad * Frequency) * Magnitude;
}
The sine wave will go from -1 to 1, the magnitude value will make it go from (-magnitude to +magnitude) Since we don't want a negative light intensity, we add magnitude to the start, so the end result is (0 to 2 * magnitude) You can change this to work however you desire, but the point should be clear.
The Frequency variable will change how fast we animate back and forth.
Related
I know how to create a sinusoidal movement with particles as per the code below. What I would like to do however is to create an effect which is more of a ripple along a string. The idea is that a wave moves along a string but the section that is not currently in a wave returns to the zero position and doesn't undergo a further wave- ie just one wave passing down the line.
How do I amend the sinusoidal movement below to achieve this?
int xspacing = 16; // How far apart should each horizontal location be spaced
int w; // Width of entire wave
float theta = 0.0; // Start angle at 0
float amplitude = 75.0; // Height of wave
float period = 500.0; // How many pixels before the wave repeats
float dx; // Value for incrementing X, a function of period and xspacing
float[] yvalues; // Using an array to store height values for the wave
void setup() {
size(640, 360);
w = width+16;
dx = (TWO_PI / period) * xspacing;
yvalues = new float[w/xspacing];
}
void draw() {
background(0);
calcWave();
renderWave();
}
void calcWave() {
// Increment theta (try different values for 'angular velocity' here
theta += 0.02;
// For every x value, calculate a y value with sine function
float x = theta;
for (int i = 0; i < yvalues.length; i++) {
yvalues[i] = sin(x)*amplitude;
x+=dx;
}
}
void renderWave() {
noStroke();
fill(255);
// A simple way to draw the wave with an ellipse at each location
for (int x = 0; x < yvalues.length; x++) {
ellipse(x*xspacing, height/2+yvalues[x], 16, 16);
}
}
I'm not totally sure exactly what you're going for. Drawing out some examples might help explain it better.
But the short answer to your question is: you'd change the height of the sin wave by modifying this line:
yvalues[i] = sin(x)*amplitude;
Right now every particle has the same amplitude, so it your wave has a uniform height. Instead, what you want to do is give each particle a different amplitude. Here's a very simple example:
yvalues[i] = sin(x) * x * 10;
This causes particles towards the left of the screen to have a smaller amplitude, and particles at the right of the screen to have a larger amplitude. In other words, the wave starts out flat and gets larger as it moves to the right.
What I would probably do is create a Particle class that encapsulates each particle's position, movement, and amplitude. Then I'd decrease the amplitude of each particle over time, maybe increasing it when the user clicks (or whatever event you want to spawn your waves).
Shameless self-promotion: I've written a tutorial on creating classes in Processing available here.
This morning I felt like writing a useless program and I ended up with this extremely minimal astronomic simulator in processing. MCVE version of code attached at the end of the post.
Then I added some planets, sit back and got ready to enjoy watching some planets orbiting around each other. However there turned out to be a problem.
The problem is, two originally static planets that getting too close to each other tend to fling each other to super ultra high speed and they will both disappear out of the screen forever. This is obviously against principle of momentum. Planets don't do this. I start to doubt there is something wrong with my Newton's Law implementation. But some other times the planets work just fine, provided that they keep a 'safe' distance.
If you wanna look into this problem, I have carefully included two setups for you. The first one, adds two planets and is beautifully stable. (You can do this by commenting out every line that has 'Three' in it.) You can test the second one by just input the default code. This one is where planets go crazy.
This issue seems like a mystery that origins from some computer science domain I have never explored(I am a noob though). For the sake of my hair, I would greatly appreciate it if someone could explain.
Start of code:
PVector planetOneLocation = new PVector(300, 200);
PVector planetOneSpeed = new PVector(0, -.1);
float planetOneMass = 1;
PVector planetTwoLocation = new PVector(100, 200);
PVector planetTwoSpeed = new PVector(0, .1);
float planetTwoMass = 1;
PVector planetThreeLocation = new PVector(200, 200);
PVector planetThreeSpeed = new PVector(0, 0);
float planetThreeMass = 10;
float g = 5;
void setup() {
size(500, 500);
}
void draw() {
updatePlanetOne();
updatePlanetTwo();
updatePlanetThree();
planetOneLocation.add(planetOneSpeed);
planetTwoLocation.add(planetTwoSpeed);
planetThreeLocation.add(planetThreeSpeed);
background(0);
ellipse(planetOneLocation.x, planetOneLocation.y, 10*planetOneMass, 10*planetOneMass);
ellipse(planetTwoLocation.x, planetTwoLocation.y, 10*planetTwoMass, 10*planetTwoMass);
ellipse(planetThreeLocation.x, planetThreeLocation.y, 10*planetThreeMass, 10*planetThreeMass);
}
void updatePlanetOne() {
PVector accDir = PVector.sub(planetTwoLocation, planetOneLocation);
float a = g * planetTwoMass / (accDir.mag() * accDir.mag());
accDir.normalize();
PVector acceleration = accDir.mult(a);
planetOneSpeed.add(acceleration);
accDir = PVector.sub(planetThreeLocation, planetOneLocation);
a = g * planetThreeMass / (accDir.mag() * accDir.mag());
accDir.normalize();
acceleration = accDir.mult(a);
planetOneSpeed.add(acceleration);
}
void updatePlanetTwo() {
PVector accDir = PVector.sub(planetOneLocation, planetTwoLocation);
float a = g * planetOneMass / (accDir.mag() * accDir.mag());
accDir.normalize();
PVector acceleration = accDir.mult(a);
planetTwoSpeed.add(acceleration);
accDir = PVector.sub(planetThreeLocation, planetTwoLocation);
a = g * planetThreeMass / (accDir.mag() * accDir.mag());
accDir.normalize();
acceleration = accDir.mult(a);
planetTwoSpeed.add(acceleration);
}
void updatePlanetThree() {
PVector accDir = PVector.sub(planetOneLocation, planetThreeLocation);
float a = g * planetOneMass / (accDir.mag() * accDir.mag());
accDir.normalize();
PVector acceleration = accDir.mult(a);
planetThreeSpeed.add(acceleration);
accDir = PVector.sub(planetTwoLocation, planetThreeLocation);
a = g * planetTwoMass / (accDir.mag() * accDir.mag());
accDir.normalize();
acceleration = accDir.mult(a);
planetThreeSpeed.add(acceleration);
}
Update: After some effort I change float variables to double. But my planets are stilling bouncing out of the screen. I think besides the double/float problem, actually there is some issues with the resolution. I didn't define any time step, and that also contribute to inaccurate behaviour especially when speed is high.
Update 2: Setting up time-step helps a lot. Some setups that went crazy without a time-step now works fine. But as long as there is the possibility that the center of two planets will be extremely close, there will be chances that the system go wild again. To solve this, a better integrator is needed.
Updata 3: In respond to #kevin-workman I ported his beautiful code here to replace the original project code. The same third planet in original post is added and the corresponding Newton math is updated. Contradictory to his tests, it looks even if mv.add(p.speed.mult(p.mass)); is commented out, the third planet still go crazy(Now since I have changed the demonstration code to a minimal version, there is no such line anymore). I think the error introduced by mult()do contribute, but concretely the unstable integrator also plays a major role.
This problem has nothing to do with float vs double accuracy. Float has more than enough accuracy for this, and in fact Processing uses float for everything by default, so any double values you try to use will just get lost anyway.
All of your problems are caused by this line:
for (Planet p: planets) {
mv.add(p.speed.mult(p.mass));
}
Particularly this bit:
p.speed.mult(p.mass)
This line multiplies each planet's speed by its mass.
You might be thinking that p.speed.mult(p.mass) will leave the original p.speed PVector unchanged, but this isn't the case. PVector is not immutable, so calling the mult() function changes the underlying PVector instance.
Your first two planets have a mass of 1, so this line doesn't really affect them. But your third planet has a mass of 10, which means you're multiplying its speed by 10 every single frame.
You can test this by simply changing the mass of one of your original planets to 10 or even 2, or by changing the mass of the third planet to 1.
So, to fix your main problem, just get rid of this line, or change it so that it doesn't modify the p.speed PVector.
More info can be found in the Processing reference for PVector#mult():
Multiplies a vector by a scalar. The version of the method that uses a
float acts directly on the vector upon which it is called (as in the
first example above). The versions that receive both a PVector and a
float as arguments are static methods, and each returns a new PVector
that is the result of the multiplication operation.
Basically, you could change that line to this:
PVector.mult(p.speed, p.mass)
That will leave p.speed unchanged and return a copy with the multiplied value.
Taking a step back, you're going to have another issue because your planets can get arbitrarily close to each other. In other words, their distance can approach (or equal) zero. This doesn't happen in real life, and if it does, you can bet things are going to go crazy. So you're going to have crazy "gravity assists" if things get too close. You might consider limiting their distances.
If you have further questions, it'll be easier to help you if you work from this MCVE instead of posting your whole project:
PVector planetOneLocation = new PVector(300, 200);
PVector planetOneSpeed = new PVector(0, -.1);
float planetOneMass = 1;
PVector planetTwoLocation = new PVector(100, 200);
PVector planetTwoSpeed = new PVector(0, .1);
float planetTwoMass = 10;
float g = 5;
void setup() {
size(500, 500);
}
void draw() {
updatePlanetOne();
updatePlanetTwo();
planetOneLocation.add(planetOneSpeed);
planetTwoLocation.add(planetTwoSpeed);
background(0);
ellipse(planetOneLocation.x, planetOneLocation.y, 10*planetOneMass, 10*planetOneMass);
ellipse(planetTwoLocation.x, planetTwoLocation.y, 10*planetTwoMass, 10*planetTwoMass);
}
void updatePlanetOne() {
PVector accDir = PVector.sub(planetTwoLocation, planetOneLocation);
float a = g * planetOneMass / (accDir.mag() * accDir.mag());
accDir.normalize();
PVector acceleration = accDir.mult(a);
planetOneSpeed.add(acceleration);
}
void updatePlanetTwo() {
PVector accDir = PVector.sub(planetOneLocation, planetTwoLocation);
float a = g * planetTwoMass / (accDir.mag() * accDir.mag());
accDir.normalize();
PVector acceleration = accDir.mult(a);
planetTwoSpeed.add(acceleration);
}
The problem is in the integrating part.
The one used is an Euler Integrator and it's not very accurate. Verlet integration is normally used for physics simulation.
Quoting Ilmari Karonen's Answer, verlet can be implemented as:
acceleration = force(time, position) / mass;
time += timestep;
position += timestep * (velocity + timestep * acceleration / 2);
newAcceleration = force(time, position) / mass;
velocity += timestep * (acceleration + newAcceleration) / 2;
I'm making a game space simulator. My spacesheep has to fly around the Earth in 3D.
I've made a simple controller for spacesheep:
public GameObject objToMove=GameObject.Find("Player");
public GameObject zeroPoint=GameObject.Find("Earth");
public float farFromZero=10;
void Update(){
Vector2 Ldirection;
Ldirection.x = Input.GetAxis ("Horizontal");
Ldirection.y = Input.GetAxis ("Vertical");
MoveObj (objToMove, Ldirection.x, Ldirection.y);
}
I also made a separated class just because i want to get control from another class (touchscreen)
public void MoveObj(GameObject LobjToMove, float AxsHoriz, float AxsVert){
AxsHoriz = AxsHoriz * speed;
AxsVert = AxsVert * speed;
AxsHoriz *= Time.deltaTime;
AxsVert *= Time.deltaTime;
LobjToMove.transform.Translate(AxsHoriz, AxsVert, 0);
//spase has to look at the earth
LobjToMove.transform.LookAt (zeroPoint.transform.position);
ZeroPointMagnitude (LobjToMove);
}
//fixing the distance between earth and spacesheep
void ZeroPointMagnitude(GameObject LobjToMove)
{
Vector3 direction = zeroPoint.transform.position - LobjToMove.transform.position;
float distance = direction.magnitude;
if (distance > farFromZero||distance < farFromZero) {
LobjToMove.transform.position += direction.normalized * (distance - farFromZero);
}
}
I've put camera in the spacesheep.
It looks good and spacesheep flies around the earth but it looks like it flying around the pivot, just making radius smaller when it reaches poluses and it can't go around if I press up or down - it stops at the bottom and upstairs
I've tried to make my it fly well for 3 months, I used Raycast or Quaternions and it doesn't work as I want it.
Looks like it going to take few months more and I ask you to help me with flying player around the object without stopping on puluses (or camera flipping)
so here we go
Vector3 rotDir=new Vector3(AxsVert,AxsHoriz,0);
float gypotenuse=Mathf.Sqrt(AxsHoriz*AxsHoriz+AxsVert*AxsVert);
gypotenuse=(gypotenuse>1)?1:gypotenuse;
objToMove.transform.RotateAround (zeroPoint.transform.position,
objToMove.transform.TransformDirection(rotDir),
gypotenuse
);
Im trying to rotate my player to face where I last clicked. I've acutally manged to do this, but now I want to see the player rotate at a set speed instead of the sprite just changing rotation instantly.
Ive tried several methods I've found online, but none of them work for me. Here's what I have
void Update()
{
if (Input.GetMouseButtonDown (0))
{
Vector3 diff = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
diff.Normalize();
float rot_z = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
transform.rotation= Quaternion.Euler(0f, 0f, rot_z - 90);
Instantiate(ProjectilePrefab, transform.position, transform.rotation);
}
}
The code above works fine, but it shows no movement. I have tried to do this but the position is wrong and the rotation is instant as well:
Vector3 diff = Camera.main.ScreenToWorldPoint(Input.mousePosition) - transform.position;
var newRotation = Quaternion.LookRotation(diff);
newRotation.y = 0.0f;
newRotation.x = 0.0f;
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, Time.deltaTime * 30);
ANy ideas?
Two problems here. First, the Slerp function is only called once after the user pressed the mouse button. You have to move it outside the if part or use a coroutine. Second, Slerp expects a float from 0 to 1 to indicate the progress and not time or deltaTime. The official examples covering the lerp functions are just bad, because using the time value would work only during the first second the game is running.
You need something like this
if (Input.GetMouseButtonDown (0)) {
// your other stuff here
float starTime = Time.time;
}
float t = Time.time - startTime;
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, t);
The rotation would finish after one second. If you want it faster or slower just multiply t with a factor.
I found the answer. This is how I made it work
if (Input.GetMouseButtonDown (0)) {
target = Camera.main.ScreenToWorldPoint (Input.mousePosition);
rotateToTarget = true;
print ("NEW TARGET: " + target);
}
if (rotateToTarget == true && target != null) {
print ("Rotating towards target");
targetRotation = Quaternion.LookRotation (transform.position - target.normalized, Vector3.forward);
targetRotation.x = 0.0f;//Set to zero because we only care about z axis
targetRotation.y = 0.0f;
player.transform.rotation = Quaternion.Slerp (transform.rotation, targetRotation, Time.deltaTime * rotationSpeed);
if (Mathf.Abs (player.transform.rotation.eulerAngles.z - targetRotation.eulerAngles.z) < 1) {
rotateToTarget = false;
travelToTarget = true;
player.transform.rotation = targetRotation;
print ("ROTATION IS DONE!");
}
}
I'm working on a 2D game in Unity3D (using Orthello 2D).
Since I switched from Cocos2d and CoronaSDK, I'm wondering if there's a way of implementing the following behaviour for a sprite (or any Unity3D object) as it worked in Corona:
object = ...
transition.to ( object, { time = 1000, rotation = object.rotation + 100, onComplete = function ()
// do something
end })
So a sprite rotates by 100 degrees over 1 second.
In my script attached to a sprite I can have a rotation in my Update () function, but it's a bit different approach...
You can do it easily in an Update function.
float timer = 0f;
void Update()
{
if(timer <= 1)
{
// Time.deltaTime*100 will make sure we are moving at a constant speed of 100 per second
transform.Rotate(0f,0f,Time.deltaTime*100);
// Increment the timer so we know when to stop
timer += Time.deltaTime;
}
}
If you need to do another 100 degrees rotation you will just have to reset the timer.
You can see different version of the Rotate function here and more information about the lifesaver Time.deltaTime value here
There are several differnt ways of doing that. For example using a coroutine:
IEnumerator TweenRotation(Transform trans, Quaternion destRot, float speed, float threshold )
{
float angleDist = Quaternion.Angle(trans.rotation, destRot);
while (angleDist > threshold)
{
trans.rotation = Quaternion.RotateTowards(trans.rotation, destRot, Time.deltaTime * speed);
yield return null;
float angleDist = Quaternion.Angle(trans.rotation, destRot);
}
}