I want to rotate this image in cocos2d Java-script HTML5.
Upper and lower circles are two different sprites.
I am using this code:
var RotationAmount=0;
Top=cc.Sprite.create("Assets/Top.png");
Top.setPosition(MidX,MidY+100);
Top.schedule(function(){
if(RotationAmount>360)
RotationAmount=0;
});
this.addChild(Top);
Here's how my sprites look:
The above this causes my sprite starts to rotate around itself. I want to make it rotate around a point, so where am I going wrong?
You can do next:
var Top = cc.Sprite.create("Assets/Top.png");
var rotateAction = cc.RotateBy.create(0, 1);
Top.attr({
x: MidX,
y: MidY,
anchorX: 0.5,
anchorY: 0.5
});
Top.schedule(function() {
Top.runAction(rotateAction);
});
this.addChild(Top);
Related
Can't wrap my head around this and get it to work. Trying to transpose this sample on Konva, but can not get it to work with an image inside a layer, inside the stage.
The sample I am trying to reproduce is the "Zooming stage relative to pointer position" sample.
https://konvajs.org/docs/sandbox/Zooming_Relative_To_Pointer.html
Any help would generate kudos.
The trick with zooming from an arbitrary point such as the mouse position is to understand that you must relocate the stage to keep the point under the mouse consistent. I provide the working vanilla JS snippet below to illustrate the point. The guts are based on the original demo at the Konva docs site, just with more comments in the code. Translating into React should not be difficult once the basis of the technique is explained.
The gray area is the canvas, and the pink rect is the stage. Note that we start with the stage positioned into the canvas just to show that the stage can be moved like any other object.
The stage has a dot-pattern to help show the effects in the demo. The solution works for any layout of layers, shapes or images (as per OP question). Note that the shapes themselves do not need to be handled individually - the scaling is applied to the stage.
Move your mouse anywhere on the stage and scroll to see the zoom effect, noting how the top-left of the pink stage rectangle moves with the zoom. Now un-tick the checkbox to turn off the stage position adjustment - note now that the zoom is no longer respecting the mouse position because the stage position is not being moved.
Thus we have illustrated that it is necessary to move the stage sympathetically during the zoom but how do we know the amount to move by?
The tip to know is that you need to get the absolute distance of the mouse position over the stage before the new scale is applied, then apply the new scale to the stage, then use the computed distance * new scale to compute the new stage position. See the stage.on('wheel') event in the snippet for working code.
See also the codepen here.
// Making the stage
let stage = new Konva.Stage({
container: "container1",
width: $('#container1').width(),
height: $('#container1').height(),
draggable: true,
x: 40,
y: 60
});
// Make the layer
let layer = new Konva.Layer();
// make a rect to fill stage to show where it is.
let rect = new Konva.Rect({
x:0,
y: 0,
width: $('#container1').width(),
height: $('#container1').height(),
fill: 'magenta',
opacity: 0.3
})
layer.add(rect);
let grid = {w: $('#container1').width(), h: $('#container1').height()},
gridStep = 40,
mouseCircleRadius = 80,
circles = [],
dotRadius = 10;
// Make the grid of circles
for (let i = gridStep; i < grid.w; i = i + gridStep ){
for (let j = gridStep; j < grid.h; j = j + gridStep ){
let c = new Konva.Circle({ x: i, y: j, radius: dotRadius, fill: 'cyan'})
circles.push(c);
layer.add(c)
}
}
// Add layer to stage
stage.add(layer)
stage.on('mousemove', function (e) {
var pointer = stage.getPointerPosition();
// show the pointer and stage positions for illustration
$('#trace').html('Pointer.x = ' + pointer.x + ' stage.x() = ' + stage.x())
});
// this is the scale factor to be applied at each step.
var scaleBy = 1.01;
// this is the event that fires when the mouse wheel spins.
stage.on('wheel', (e) => {
e.evt.preventDefault();
// note the old scale to be used when deciding the current stage position
var oldScale = stage.scaleX();
// note the mouse pointer position relative to the stage at current scale
var pointer = stage.getPointerPosition();
// calculate the mouse pointer position at scale = 1.
// pointer.x is relative to the canvas - not affected by stage scale,
// stage.x() is the poistion of the stage on the canvas.
// This gives the distance from the pointer to the
var mousePointTo = {
x: (pointer.x - stage.x()) / oldScale,
y: (pointer.y - stage.y()) / oldScale,
};
// Calculate the new scale - slightly different calc for zoom in versus zoom out, as you would expect.
var newScale = (e.evt.deltaY > 0 ? oldScale * scaleBy : oldScale / scaleBy);
// Apply the new scale to the stage. Note that, assuming a zoom-in op, at this point in time the shapes on the stage would
// seem to have jumped down + right. We will shortly 'move' the stage left+up to counter this effect.
stage.scale({ x: newScale, y: newScale });
// To do that we have to calculate what the position of the stage must be relative to the mouse pointer position at the new scale.
// Note - remove the 'if' for your live code, the checkbox is for illustration only.
if ($('#fixstagepos').prop('checked')){
var newPos = {
x: pointer.x - mousePointTo.x * newScale,
y: pointer.y - mousePointTo.y * newScale,
};
// and apply the new position to the stage. Again in the case of a zoom-in op this has the effect of moving the stage left + up, countering the apparent movement
// caused by the change in scale.
stage.position(newPos);
}
});
.container {
width: 800px;
height: 400px;
background-color: silver;
margin: 5px;
}
#trace {
max-height: 200px;
overflow-y: scroll;
font-family: 'Courier'
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://unpkg.com/konva#8/konva.min.js"></script>
<p id="info">Grey is the canvas and the pink rect shows the stage position. The stage is intitially positioned at {x: 40, y: 60} and can be dragged.</p>
<p>Move mouse over stage and scroll to zoom in & out. Use checkbox to see what happens without stage pos correction. </p>
<p>Apply correction to stage position <input id='fixstagepos' type='checkbox' checked='1' /></p>
<div id='container1' class='container'>
</div>
<p id='trace'></p>
I have some camera with perspective projection and object in the (0,0,0).
I computed "bounding frame"(size in pixels on screen) of this object according to this camera and scaled it to fit the screen size. And now it in the middle of screen.
Frame of the scnView is equal to the screen size and I have some UI components above this view(e.g. navigation bar and some big transparent view in the bottom of view). The scnView has some fullscreen background and this object can be scaled/moved/... therefore I need scnview to be fullscreen.
Then I move this object in the middle of "clear area" (the area with absence of UIComponents) and see the bottom of this object (as if it's above me), because my projection is perspective.
I want to move this object in the center of clear area and see like it's in front of me without any distortion. How can I achieve that?
I see 2 solutions.
1) Draw my object offscreen in texture and after that draw this texture on screen in required position.
2) create scnView a bit taller. For example set frame size to = (0, (bottomHeight - topHeight) / 2, width, height + (bottomHeight - topHeight) / 2) to move the center of scnView in required position.
But I don't like first solution due to addition draw call and second solution even sounds crappy.
P.S. sorry for that pictures
Update: Basically I want to use perspective projection and move 3D object like a 2D image in SceneKit.
Sorry if I'm not be understanding correctly, but can you strafe camera up and center it, or will that not be clear enough?
func strafeY(vAmount: XFloat)
{
nPosition = cgVecAdd(v1: nPosition, v2: cgVecScalarMult(v: nTarget, s: vAmount))
}
Try this... generate a new scenekit game project, then replace the default rotation with the code below.
Rotate X slightly as you move the object up to to "somewhat" maintain the relationship to the eye.
You could create a similar but separate routine to do smaller increments to smooth it out.
ship.runAction(SCNAction.rotateBy(x: CGFloat(GLKMathDegreesToRadians(-90)), y: 0, z: 0, duration: 0))
let vRotateAmount: Float = 3
let vAction1 = SCNAction.move(to: SCNVector3Make(0, 1, 0), duration: 1)
let vAction1a = SCNAction.rotateBy(x: CGFloat(GLKMathDegreesToRadians(vRotateAmount)), y:0, z:0, duration: 1)
let vAction2 = SCNAction.move(to: SCNVector3Make(0, 2, 0), duration: 1)
let vAction2a = SCNAction.rotateBy(x: CGFloat(GLKMathDegreesToRadians(vRotateAmount)), y:0, z:0, duration: 1)
let vAction3 = SCNAction.move(to: SCNVector3Make(0, 1, 0), duration: 1)
let vAction3a = SCNAction.rotateBy(x: CGFloat(GLKMathDegreesToRadians(-vRotateAmount)), y:0, z:0, duration: 1)
let vAction4 = SCNAction.move(to: SCNVector3Make(0, 0, 0), duration: 1)
let vAction4a = SCNAction.rotateBy(x: CGFloat(GLKMathDegreesToRadians(-vRotateAmount)), y:0, z:0, duration: 1)
let seq = SCNAction.sequence([vAction1, vAction1a, vAction2, vAction2a, vAction3, vAction3a, vAction4, vAction4a])
let allSeq = SCNAction.repeatForever(seq)
ship.runAction(allSeq)
Solved it by changing camera projectionTransform. Smth like:
let newMatrix = SCNMatrix4Mult(initialProjectionCameraMatrix, translationMatrix)
I am using this function to animate a rotation of a SCNNode:
let rotateNode = SCNAction.rotateTo(x: 0.0, y: CGFloat((headingAngle )), z: 0.0, duration: TimeInterval(1.1), usesShortestUnitArc: true)
node.runAction(rotateNode)
It works really well for different directions except for when I need to rotate it towards the user (or the camera for that matter).
My question is how to direct the SCNNode to move towards the user/camera and how to calculate the headingAngle in order to rotate the SCNNode to face the user/camera when it is moving.
I perform the moving by using
let impulseVector = SCNVector3(
x: 0.0 ,
y: 5.0,
z: 0.0
)
node.physicsBody?.applyForce(impulseVector, at: positionOnNodeToApplyForceTo, asImpulse: true) // propell
And I am aware that the headingAngle needs to be calculated using atan2 function, but for some reason I do not manage to properly direct the SCNNode to move towards the camera nor to rotate and face the camera.
I have a spotlight, created with the code beneath, casting shadows on all of my nodes:
spotLight.type = SCNLightTypeSpot
spotLight.spotInnerAngle = 50.0
spotLight.spotOuterAngle = 150.0
spotLight.castsShadow = true
spotLight.shadowMode = SCNShadowMode.Deferred
spotlightNode.light = spotLight
spotlightNode.eulerAngles = SCNVector3(x: GLKMathDegreesToRadians(-90), y: 0, z: 0)
spotlightNode.position = levelData.coordinatesForGridPosition(column: 0, row: playerGridRow)
spotlightNode.position.y = 1.5
rootNode.addChildNode(spotlightNode)
The scene is moving along the z axis, and the camera has an infinite animation that makes it move:
let moveAction = SCNAction.moveByX(0.0, y: 0.0, z: CGFloat(-GameVariables.segmentSize / 2), duration: 2.0)
cameraContainerNode.runAction(SCNAction.repeatActionForever(moveAction))
As the camera moves though, the light doesn't, so after a while, the whole scene is dark. I want to move the light with the camera, however if I apply to the light node the same moving animation, all the shadows start to flicker. I tried to change the SCNShadowMode to Forward and the light type to Directional, but the flickering is still there. With directional, I actually loose most of my shadows. If I create a new light node later on, it will seem that I have two "suns", which of course is impossible. The final aim is simply to have an infinite light that shines parallel to the scene from the left, casting all the shadows to the right. Any ideas?
Build a node tree to hold both spotlight and camera.
Create, say, cameraRigNode as an SCNNode with no geometry. Create cameraContainerNode and spotlightNode the same way you are now. But make them children of cameraRigNode, not the scene's root node.
Apply moveAction to cameraRigNode. Both the camera and the light will now move together.
Before writing this question, I've
had experience with Affine transforms for views
read the Transforms documentation in the Quartz 2D Programming Guide
seen this detailed CALayer tutorial
downloaded and run the LayerPlayer project from Github
However, I'm still having trouble understanding how to do basic transforms on a layer. Finding explanations and simple examples for translate, rotate and scale has been difficult.
Today I finally decided to sit down, make a test project, and figure them out. My answer is below.
Notes:
I only do Swift, but if someone else wants to add the Objective-C code, be my guest.
At this point I am only concerned with understanding 2D transforms.
Basics
There are a number of different transforms you can do on a layer, but the basic ones are
translate (move)
scale
rotate
To do transforms on a CALayer, you set the layer's transform property to a CATransform3D type. For example, to translate a layer, you would do something like this:
myLayer.transform = CATransform3DMakeTranslation(20, 30, 0)
The word Make is used in the name for creating the initial transform: CATransform3DMakeTranslation. Subsequent transforms that are applied omit the Make. See, for example, this rotation followed by a translation:
let rotation = CATransform3DMakeRotation(CGFloat.pi * 30.0 / 180.0, 20, 20, 0)
myLayer.transform = CATransform3DTranslate(rotation, 20, 30, 0)
Now that we have the basis of how to make a transform, let's look at some examples of how to do each one. First, though, I'll show how I set up the project in case you want to play around with it, too.
Setup
For the following examples I set up a Single View Application and added a UIView with a light blue background to the storyboard. I hooked up the view to the view controller with the following code:
import UIKit
class ViewController: UIViewController {
var myLayer = CATextLayer()
#IBOutlet weak var myView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// setup the sublayer
addSubLayer()
// do the transform
transformExample()
}
func addSubLayer() {
myLayer.frame = CGRect(x: 0, y: 0, width: 100, height: 40)
myLayer.backgroundColor = UIColor.blue.cgColor
myLayer.string = "Hello"
myView.layer.addSublayer(myLayer)
}
//******** Replace this function with the examples below ********
func transformExample() {
// add transform code here ...
}
}
There are many different kinds of CALayer, but I chose to use CATextLayer so that the transforms will be more clear visually.
Translate
The translation transform moves the layer. The basic syntax is
CATransform3DMakeTranslation(_ tx: CGFloat, _ ty: CGFloat, _ tz: CGFloat)
where tx is the change in the x coordinates, ty is the change in y, and tz is the change in z.
Example
In iOS the origin of the coordinate system is in the top left, so if we wanted to move the layer 90 points to the right and 50 points down, we would do the following:
myLayer.transform = CATransform3DMakeTranslation(90, 50, 0)
Notes
Remember that you can paste this into the transformExample() method in the project code above.
Since we are just going to deal with two dimensions here, tz is set to 0.
The red line in the image above goes from the center of the original location to the center of the new location. That's because transforms are done in relation to the anchor point and the anchor point by default is in the center of the layer.
Scale
The scale transform stretches or squishes the layer. The basic syntax is
CATransform3DMakeScale(_ sx: CGFloat, _ sy: CGFloat, _ sz: CGFloat)
where sx, sy, and sz are the numbers by which to scale (multiply) the x, y, and z coordinates respectively.
Example
If we wanted to half the width and triple the height, we would do the following
myLayer.transform = CATransform3DMakeScale(0.5, 3.0, 1.0)
Notes
Since we are only working in two dimensions, we just multiply the z coordinates by 1.0 to leave them unaffected.
The red dot in the image above represents the anchor point. Notice how the scaling is done in relation to the anchor point. That is, everything is either stretched toward or away from the anchor point.
Rotate
The rotation transform rotates the layer around the anchor point (the center of the layer by default). The basic syntax is
CATransform3DMakeRotation(_ angle: CGFloat, _ x: CGFloat, _ y: CGFloat, _ z: CGFloat)
where angle is the angle in radians that the layer should be rotated and x, y, and z are the axes about which to rotate. Setting an axis to 0 cancels a rotation around that particular axis.
Example
If we wanted to rotate a layer clockwise 30 degrees, we would do the following:
let degrees = 30.0
let radians = CGFloat(degrees * Double.pi / 180)
myLayer.transform = CATransform3DMakeRotation(radians, 0.0, 0.0, 1.0)
Notes
Since we are working in two dimentions, we only want the xy plane to be rotated around the z axis. Thus we set x and y to 0.0 and set z to 1.0.
This rotated the layer in a clockwise direction. We could have rotated counterclockwise by setting z to -1.0.
The red dot shows where the anchor point is. The rotation is done around the anchor point.
Multiple transforms
In order to combine multiple transforms we could use concatination like this
CATransform3DConcat(_ a: CATransform3D, _ b: CATransform3D)
However, we will just do one after another. The first transform will use the Make in its name. The following transforms will not use Make, but they will take the previous transform as a parameter.
Example
This time we combine all three of the previous transforms.
let degrees = 30.0
let radians = CGFloat(degrees * Double.pi / 180)
// translate
var transform = CATransform3DMakeTranslation(90, 50, 0)
// rotate
transform = CATransform3DRotate(transform, radians, 0.0, 0.0, 1.0)
// scale
transform = CATransform3DScale(transform, 0.5, 3.0, 1.0)
// apply the transforms
myLayer.transform = transform
Notes
The order that the transforms are done in matters.
Everything was done in relation to the anchor point (red dot).
A Note about Anchor Point and Position
We did all our transforms above without changing the anchor point. Sometimes it is necessary to change it, though, like if you want to rotate around some other point besides the center. However, this can be a little tricky.
The anchor point and position are both at the same place. The anchor point is expressed as a unit of the layer's coordinate system (default is 0.5, 0.5) and the position is expressed in the superlayer's coordinate system. They can be set like this
myLayer.anchorPoint = CGPoint(x: 0.0, y: 1.0)
myLayer.position = CGPoint(x: 50, y: 50)
If you only set the anchor point without changing the position, then the frame changes so that the position will be in the right spot. Or more precisely, the frame is recalculated based on the new anchor point and old position. This usually gives unexpected results. The following two articles have an excellent discussion of this.
About the anchorPoint
Translate rotate translate?
See also
Border, rounded corners, and shadow on a CALayer
Using a border with a Bezier path for a layer