I'm making a game in Unity and I want to move an object a certain distance when I click a button and then stop until I click it again. I have tried with the Lerp function but I can't make the desired effect correctly
If you wanted to do this instantly then that's easy enough, you simply detect for mouse input and then move the object in whatever direction you want it to. Here's some sample c# code to do just that (In the update function).
if(Input.GetMouseButtonDown(0))
{
transform.Translate(Vector3ToMove);
}
The variable Vector3ToMove would be a Vector3 storing the vector that you want it to move relative to it's current position. This is functional however it's not smooth and isn't particularly pleasing to look at. In order to achieve smooth movement you need to use a Lerp function, this lerp function needs to be called for multiple frames for it to reach the position, that may have been where you went wrong before. Here's some sample code to achieve the smooth effect using Vector3.Lerp
Vector3 TargetPosition;
float speed = 1f;
void Start()
{
TargetPosition = transform.position;
}
void Update()
{
if(Input.GetMouseButtonDown(0))
{
TargetPosition += Vector3ToMove;
}
transform.position = Vector3.Lerp(transform.position, TargetPosition, speed * Time.deltaTime);
}
Vector3ToMove just like the previous example is the vector increment that you want to apply to the objects position. You may want to tweak the speed variable in the example provided there to get a nice smooth speed, you can also try using Vector3.SmoothDamp to get a slightly different smoothing effect.
Related
I am using create.js's tween.js. I am trying to make a bounce animation, more complex than the one given. More specifically, I am trying to tween an objects's y by an offset rather than a given coordinate. It seems like there is a simple way of doing this like:
createjs.Tween.get(circle, {loop:false}).to({offset-x: 100}, 1000, createjs.Ease.linea);
Ultimate Question: How do I tween the coordinates of an object based off of its current position?
When you make a Tween instance, you are creating a deterministic animation, meaning that you can set the position of the tween to any value (between 0 and 1), and it will be where you expect it.
If I understand what you are asking, you want to make a tween up-front, but have it use the object's current position instead of the value it was at when the tween was made. If this is the case, then you will need a different approach. Perhaps you should generate the tween on-demand when it is needed, rather than initially. That way, it could use your current position.
Here is a quick sample: http://jsfiddle.net/x4xxwjuv/
The ball moves at a constant rate, but when you click the buttons, it will:
Tween back a bit, based on its current position
OR Tween to a specific Y position (fall to the floor)
Then it resumes moving normally. The tweened ball is filled, the animated one is not.
Here is sample code from the spike.
createjs.Tween.get(ball)
.to({x: ball.x+(ball.xSpeed*-4), y:ball.y+(ball.ySpeed*-4)}, 1000, createjs.Ease.bounceOut);
I have a dynamically generated svg image that I am using Ariutta's svg-pan-zoom plugin with. When I double click an svg image, I set pan.x = centerOfScreenX, and pan.y = centerOfScreenY to center the image in the middle of the screen. ie:
$('.svg').dblclick(function(){
zoom.pan({'x':centerOfScreenX, 'y':centerOfScreenY });
});
Currently this causes the image to just suddenly move to the center of the screen. Is there a way I can animate this change in pan position so that the image doubleclicked moves along a path to the center of the screen instead?
Bumbu suggested two solution paths (see answers below), and I have taken a stab at the first. My attempt did not work however, and I do not know why.
// centerOfScreenX and centerOfScreenY are the correct values that pan.x and
// pan.y should have to center the svg in the middle of the screen
// xInterval and yInterval break the distance between the current pan
// position and the desired pan position into 10 steps
var xInterval = (centerOfScreenX - pan.x)/10;
var yInterval = (centerOfScreenY - pan.y)/10;
while( pan.x !== centerOfScreenX && pan.y !== centerOfScreenY ){
if(pan.x !== centerOfScreenX){
pan({'x': pan.x + xInterval })
}
if(pan.y !== centerofScreenY){
pan({'y': pan.y + yInterval })
}
}
When I try to run this code, the window freezes and I can no longer interact with my app, unless i close the window and reload it. My guess is that I am somehow triggering an infinite loop.
Currently there is no solution to do animation in an easy way.
There is a similar question (about animating zoom). The answer from there (adjusted to this one) is:
Currently such functionality is not supported. You could do it in 2 ways:
Use a twin library (or write you own function) and just call pan in small iterations multiple times. This may be slow but it is what many libraries do when implementing animation (eg. jQuery).
Use SVG animateTransform element. It seems to be the right way. But it needs some work to get it done.
You can actually try to implement second solution by listening to zoom
events, canceling them and adding animateTransform manually to the SVG.
When your animation is done, call zoom again but this time don't
cancel it (necessary to update library inner state).
There is an ongoing discussion about next version of library that would be more extensible. This would allow to write plugins. Animation is one of the candidates. But it will take some time (few months) to do this.
If you'll be able to find a temporary solution - share it here or on github and we'll be happy to update the library or integrate it in next version.
Edit
I added a simple example how this kind of animation can be implemented.
You can find it in demo/simple-animation.html
I used a simple interval there. A more advanced version should take into account how much time passed since last interval call and send the right amount for pan. But even like this it works very well.
The library internally uses requestAnimationFrame so you can call panBy even every millisecond and it shouldn't block the browser.
I am currently experimenting with parallax effect that i am planning to implement to my HTML5-canvas game engine.
The effect itself is fairly easy to achieve, but when you add zooming and rotating, things get a little more complicated, at least for me. My goal is to achieve something like this:Youtube video.
As you can see, you can zoom in and out "to the center", and also rotate around it and get the parallax effect.
In my engine i want to have multiple canvases that are going to be my parallax layers, and i am going to translate them.
I came up with something like this:
var parallax = {
target: {
x: Mouse.x,
y: Mouse.y
},
offset: {
x: -ctx.width / 2,
y: -ctx.height / 2
},
factor: {
x: 1,
y: 1
}
}
var angle = 0;
var zoomX = 1;
var zoomY = 1;
var loop = function(){
ctx.canvas.width = ctx.canvas.width; //Clear the canvas.
ctx.translate(parallax.target.x * parallax.factor.x, parallax.target.y * parallax.factor.y);
ctx.rotate(angle);
ctx.scale(zoomX, zoomY);
ctx.translate((-parallax.target.x - parallax.offset.x) * parallax.factor.x, (-parallax.target.y - parallax.offset.y) * parallax.factor.y);
Draw(); //Function that draws all the objects on the screen.
}
This is a very small and simplified part of my script, but i hope that's enough to get what i am doing. The object "parallax" contains the target position, the offset(the distance from the target), and the factor that is determining how fast the canvas is moving away relatively to the target. ctx is the canvas that is moving in the opposite direction of the target.(In this example i am using only one layer.) I am using the mouse as the "target", but i could also use the player, or some other object with x and y property. The target is also the point around which i rotate and scale the canvas.
This method works completely fine as long as the factor is equal to 1. If it is something else, the whole thing suddenly stops working correctly, and when i try to zoom, it zooms to the top-left corner, not the target. I also noticed that if i zoom out too much, the canvas is not moving in the opposite way of the target, but in the same direction.
So my question is: What is the correct way of implementing parallax with zooming and rotating?
P.S. It is important to me that i am using canvases as the layers.
To prepare for the next animation frame, you must undo any previous transforms in the reverse order they were executed:
context.translate(x,y);
context.scale(sx,sy);
context.rotate(r);
// draw stuff
context.rotate(-r);
context.scale(-sx,-sy);
context.translate(-x,-y);
Alternatively, you can use context.save / context.restore to undo the previous transforms.
Adjust your parallax values for the current frame,
Save the un-transformed context state using context.save(),
Do your transforms (translate, scale, rotate, etc),
Draw you objects as if they were in non-transformed space with [0,0] at your translate point,
Restore your context to it's untransformed state using context.restore()/
Either way will correctly give you a default-oriented canvas to use for your next animation frame.
The exact parallax effects you apply are up to your own design, but using these methods will make the canvas return to a normal default state for you to design with.
deviantART muro has a set of brilliant tools of painting. And I'm very curious how to implement these brushes like Sketch and Paintbrush, arithmetically?
Using any normal programming language to explain is okay, though I prefer C++ or JavaScript. I think it's better than read their JS source code.
I'd say it works something like:
Track mouse movement
On captured mouse movement, draw your desired brush from saved "Old mouse position" to captured "New mouse position", iterating at a pixel's distance at a time
If you move the mouse too fast for the script to capture, it will just look like a computed long straight line (which is what it looks like Muro is doing). If you want to get real fancy you can calculate the trajectory from previous mouse positions and draw that instead for a "smoother" line.
Since you specified Javascript you'd probably want to draw it in a canvas object.
EDIT 1:
Sketch specifically seems to save mouse movements and then loop through, say the 20 latest mouse movements for each mouse movement and draw a bezier curve from that point to the current point.
So, something like (pseudo code)
Object mousemovements = [];
on.mousemove(event)
{
if (mousemovements.length > 20)
{
mousemovements.removeLast();
}
mousemovements.insertAtBeginning([ event.mouseX, event.mouseY ]);
for-each (movement in mousemovements)
{
drawBeziercurveFromTo(movement.mouseX, movement.mouseY,
event.mouseX, event.mouseY);
}
}
Jquery/Canvas DEMO based on the above pseudo code
EDIT 2:
I had a closer look at how "Sketch" worked and it seems that they update the mouse pointer positions, moving the older points closer to the newer points. Something like this:
This DEMO works pretty much like the sketch brush
I have an inner div inside an outer div. The inner div is draggable and outer is rotated through 40 degree. This is a test case. In an actual case it could be any angle. There is another div called point which is positioned as shown in the figure. ( I am from a flash background . In Flash if I were to drag the inner div it would follow the mouse even if its contained inside an outer rotated div.) But in HTML the inner div does not follow the mouse as it can be seen from the fiddle. I want the div 'point' to exactly follow the mouse. Is this possible. I tried to work it using trignometry but could not get it to work.
http://jsfiddle.net/bobbyfrancisjoseph/kB4ra/8/
Here is my approach to this problem.
http://jsfiddle.net/2X9sT/21/
I put the point outside the rotated div. That way I'm assured that the drag event will produce a normal behavior (no jumping in weird directions). I use the draggable handler to attach the point to the mouse cursor.
In the drag event, I transform the drag offset to reflect the new values. This is done by rotating the offset around the outer div center in the opposite direction of the rotation angle.
I tested it and it seems to be working in IE9, Firefox, and Chrome.
You can try different values for angle and it should work fine.
I also modified the HTML so it is possible to apply the same logic to multiple divs in the page.
Edit:
I updated the script to account for containment behavior as well as cascading rotations as suggested in the comments.
I'm also expirementing with making the outer div draggable inside another div. Right now it is almost working. I just need to be able to update the center of the dragged div to fix the dragging behavior.
Try Dragging the red div.
http://jsfiddle.net/mohdali/kETcE/39/
I am at work now, so I can't do the job for you, but I can explain the mathematics behind the neatest way of solving your problem (likely not the easiest solution, but unlike some of the other hacks it's a lot more flexible once you get it implemented).
First of all you must realize that the rotation plugin you are using is applying a transformation to your element (transform: rotate(30deg)), which in turn is changed into a matrix by your browser (matrix(0.8660254037844387, 0.49999999999999994, -0.49999999999999994, 0.8660254037844387, 0, 0)).
Secondly it is necessary to understand that by rotating an element the axis of the child elements are rotate absolutely and entirely with it (after looking for a long time there isn't any real trick to bypass this, which makes sense), thus the only way would be to take the child out of the parent as some of the other answers suggest, but I am assuming this isn't an option in your application.
Now, what we thus need to do is cancel out the original matrix of the parent, which is a two step process. First we need to find the matrix using code along the following lines:
var styles = window.getComputedStyle(el, null);
var matrix = styles.getPropertyValue("-webkit-transform") ||
styles.getPropertyValue("-moz-transform") ||
styles.getPropertyValue("-ms-transform") ||
styles.getPropertyValue("-o-transform") ||
styles.getPropertyValue("transform");
Next the matrix will be a string as shown above which you would need to parse to an array with which you can work (there are jquery plugins to do that). Once you have done that you will need to take the inverse of the matrix (which boils down to rotate(-30deg) in your example) which can be done using for example this library (or your math book :P).
Lastly you would need to do the inverse matrix times (use the matrix library I mentioned previously) a translation matrix (use this tool to figure out how those look (translations are movements along the x and y axis, a bit like left and top on a relatively positioned element, but hardware accelerated and part of the matrix transform css property)) which will give you a new matrix which you can apply to your child element giving you the a translation on the same axis as your parent element.
Now, you could greatly simplify this by doing this with left, top and manual trigonometry1 for specifically rotations only (bypassing the entire need for inverse matrices or even matrices entirely), but this has the distinct disadvantage that it will only work for normal rotations and will need to be changed depending on each specific situation it's used in.
Oh and, if you are now thinking that flash was a lot easier, believe me, the way the axis are rotated in HTML/CSS make a lot of sense and if you want flash like behavior use this library.
1 This is what Mohamed Ali is doing in his answer for example (the transformOffset function in his jsFiddle).
Disclaimer, it has been awhile since I have been doing this stuff and my understanding of matrices has never been extremely good, so if you see any mistakes, please do point them out/fix them.
For Webkit only, the webkitConvertPointFromPageToNode function handles the missing behavior:
var point = webkitConvertPointFromPageToNode(
document.getElementById("outer"),
new WebKitPoint(event.pageX, event.pageY)
);
jsFiddle: http://jsfiddle.net/kB4ra/108/
To cover other browsers as well, you can use the method described in this StackOverflow answer: https://stackoverflow.com/a/6994825/638544
function coords(event, element) {
function a(width) {
var l = 0, r = 200;
while (r - l > 0.0001) {
var mid = (r + l) / 2;
var a = document.createElement('div');
a.style.cssText = 'position: absolute;left:0;top:0;background: red;z-index: 1000;';
a.style[width ? 'width' : 'height'] = mid.toFixed(3) + '%';
a.style[width ? 'height' : 'width'] = '100%';
element.appendChild(a);
var x = document.elementFromPoint(event.clientX, event.clientY);
element.removeChild(a);
if (x === a) {
r = mid;
} else {
if (r === 200) {
return null;
}
l = mid;
}
}
return mid;
}
var l = a(true),
r = a(false);
return (l && r) ? {
x: l,
y: r
} : null;
}
This has the disadvantage of not working when the mouse is outside of the target element, but it should be possible to extend the area it covers by an arbitrary amount (though it would be rather hard to guarantee that it covers the entire window no matter how large).
jsFiddle: http://jsfiddle.net/kB4ra/122/
This can be extended to apply to #point by adding a mousemove event:
$('#outer').mousemove(function(event){
var point = convertCoordinates(event, $("#outer"));
$("#point").css({left: point.x+1, top: point.y+1});
});
Note that I adjust the x and y coordinates of #point by 1px to prevent it from being directly underneath the mouse; if I didn't do that, then it would block dragging #inner. An alternative fix would be to add handlers to #point that detect mouse events and pass them on to whichever element is directly underneath #point (and stopPropagation, so that they don't run twice on larger page elements).
jsFiddle: http://jsfiddle.net/kB4ra/123/
It seems to me that if you do not rotate the div, the div exactly follows the mouse.
This might be a problem with the plugin..maybe you could simulate the draggable function corretly?
This basically will do what you need though it is buggy. Bind the drag event handler, intercept the ui object and modify it to use the offset X and Y of the parent element. All of the X, Y, top, left etc. are in those objects. I will try to get you a better example sometime when today when I get a bit more time. Good luck!
http://jsfiddle.net/kB4ra/107/
may be this is issue of your jquery library or you can check this by assigning z-order value of inner div and outer div make sure that you give higher number to inner div.