Mimic Soul Knight control system using JS and Unity - javascript

I am trying to imitate the control system in Soul Knight. If you are unfamiliar, whenever a player places their finger on the screen, the joystick appears underneath their finger, then if they drag their finger the joystick tracks to it. This way you dont have to be super precise and worry about putting your finger in the perfect place for the joystick.
Controls graphic
End Goal:
In the attached graphic i show what i would like to happen. Pretend the circles outside of the grey circle are the point where the user is touching with their finger. When the touch point moves as shown, i want the white circle within the gray circle to move as shown, in a circular arc to the left. I want this functionality but also want the circle to be able to move to anywhere within the gray circle if the user is touching there.
Movement system:
As is, the current system finds the position of the touch and finds the difference between that and the center circle in both the x and y axis, then it divides both of those numbers by 10, then adds both values to the x and y axis. This way, the circle moves directly towards the touch.
Constraint system:
I am using if statements and the Pythagorean theorem to ask the question "If your hypotenuse WOULD be greater than the radius of the big circle, then dont move." This effectively stops the circle, but once it is stopped it never moves again.
Other algorithms that would work better?
Any code-specific ideas?
Any content you can point me to that already works like the system i desire?
Context for the code- "base" is the grey circle
Trackme is a public var I set to tell the white circle what object to move towards.
#pragma strict
private var itemPos:Vector3;
private var basePos:Vector3;
private var moveCount = 0;
private var seed = 1;
private var xdif = 0.0f;
private var ydif = 0.0f;
private var xdifbase = 0.0f;
private var ydifbase = 0.0f;
private var radiuscircle = 13;
private var parental : GameObject;
private var base : GameObject;
var trackme : String;
function Update () {
parental = GameObject.Find(trackme);
base = GameObject.Find("joystickbase");
itemPos = parental.transform.position;
basePos = base.transform.position;
xdif = Mathf.Abs(itemPos[0] - transform.position[0])/10.0f;
ydif = Mathf.Abs(itemPos[1] - transform.position[1])/10.0f;
xdifbase = basePos[0] - transform.position[0];
ydifbase = basePos[1] - transform.position[1];
if (transform.position[0] < itemPos[0] )
{
if (Mathf.Sqrt((xdifbase+xdif)*(xdifbase+xdif) + ydifbase * ydifbase) < radiuscircle)
{
transform.position = Vector3(transform.position[0] +xdif, transform.position[1], transform.position[2]);
}
}
if (transform.position[0] > itemPos[0] )
{
if (Mathf.Sqrt((xdifbase-xdif)*(xdifbase-xdif) + ydifbase * ydifbase) < radiuscircle)
{
transform.position = Vector3(transform.position[0] -xdif, transform.position[1], transform.position[2]);
}
}
if (transform.position[1] < itemPos[1] )
{
if (Mathf.Sqrt(xdifbase * xdifbase + (ydifbase+ydif)*(ydifbase+ydif)) < radiuscircle)
{
transform.position = Vector3(transform.position[0] , transform.position[1] +ydif, transform.position[2]);
}
}
if (transform.position[1] > itemPos[1] )
{
if (Mathf.Sqrt(xdifbase * xdifbase + (ydifbase-ydif)*(ydifbase-ydif)) < radiuscircle)
{
transform.position = Vector3(transform.position[0] , transform.position[1] -ydif, transform.position[2]);
}
}
}

One way to solve this would be to instead use Mathf.Clamp on the transform's position to clamp it within the bounds of your circle. You could accomplish this by first normalizing the touch position from the center location of the circle, then multiply by your scalar, and clamp this value within your circle's bounds. You would then set your transform.position to the clamped values of this new vector. For example:
Vector2 normedVec = touch.position.normalized * 10;
transform.position = new Vector2(Mathf.Clamp(normedVec.x, -20, 20), Mathf.Clamp(normedVec.y, -20, 20);

Related

Pixel offset when there is multiple children

Found a strange behaviour that when I keep adding or toggle display sprite object. That makes my game screen shakes when toggle some of optional UIs .
Tried to reproduce this issue with simplified codes. Here is the experiment.
The red line shows the original position of first square.png
When I keep adding sprites on the same position, appears that pixels moved, the square not stack up in different positions
When hiding sprite except the first square, seems it back to correct position
var MyScene = cc.Scene.extend({
onEnterTransitionDidFinish: function(){
this._super();
this.initComponents();
},
initComponents: function () {
//create additional square container
var node = new cc.Node();
this.node = node;
node.setPosition(90, 90);
this.attach(node);
this.addChild(node);
//draw first square
var sprite = new cc.Sprite("square.png");
sprite.setPosition(50,50);
this.addChild(sprite);
//listen to keyboard, add square / toggle
if ('keyboard' in cc.sys.capabilities) {
cc.eventManager.addListener({
event: cc.EventListener.KEYBOARD,
onKeyPressed: function (keyCode, event) {
switch (keyCode) {
//toggle additional squares
case cc.KEY.q:
this.node.setVisible(!this.node.isVisible());
break;
//attach more squares
case cc.KEY.w:
this.attach(node);
break;
}
}.bind(this)
}, this);
}
//draw measure line
var line = new cc.DrawNode();
line.drawRect(cc.p(130,0), cc.p(132,150),cc.color(255,0,0,255),0);
this.addChild(line);
},
/**
* Attach more squares
* #param node
*/
attach: function (node) {
var xp = 0;
var yp = 0;
for (var i = 0; i < 5; i++) {
var sp = new cc.Sprite("square.png");
node.addChild(sp);
sp.setPosition(xp * 180, yp * 180);
xp++;
if (xp > 15) {
xp = 0;
yp++;
}
}
}
});
It's difficult to answer you without the context, what API do you use ? What is cc ?
Anyway, by comparing the screenshots you provided it seems that your squares effectively moved away. The strange corners appears because you don't clear the screen between each frame.
This produce a trail, it's not the pixels that moved, but the squares themselves.
Try those steps :
print their positions each time you add a new sprite
fix the length of view
try to print a square instead of using an image
Edit your question if you have more information, and then I will edit this answer according to your additional information.
Found that the issue can be improved / resolve by assign everything into negative z order
var children = this.getChildren();
var avicinarAka = -99999;
for (var k in children) {
if (children[k]) {
children[k].setLocalZOrder(avicinarAka++);
}
}

How to calculate the new XY coords and rotation every frame based on the speed of two motors?

Im trying to figure out how to simulate where the new XY coords and rotation will be based on the speed of two motors. e.g if the left wheel is at 50% speed and the right wheel is at 80% speed then it will go forwards but slightly to the left.
I'm using JavaScript to draw onto a canvas.
function setMotors(left, right){
var motorLoop = setTimeout(() =>{
rotation = //new rotation
x = //new x
y = //new y
clearCvs();
drawRobot(x, y, rotation);
}, 1000/60);
}
I see it like this:
where l0,l1 are the arclengths traveled by wheels, d is distance between wheels and r is turning radius of the first wheel leading to system:
omg * r = v0
omg *(r+d) = v1
where omg is the turn speed [rad/s] so (unless I made a math mistake):
r = (v0*d)/(v1-v0) [units]
omg = (v1-v0)/d [rad/s]
using your values:
r = (50*30)/(80-50) = 50.0 [pixels]
omg = (80-50)/30 = 1.0 [rad/s] = ~57.3 [deg/s]
Ignoring all possible grip/drift issues ... Signs of the results determine on which side the robot turns ...

Animating a curved motion of an object with svg.js

I want to animate a curved motion (no rotation) of an object by using svg.js. But I can't find any easy solution for this problem. I wrote two little functions which work fine, but it isn't working like a normal animation, and it doesn't run perfectly in the background.
I would prefer some solution like this:
var draw = SVG("drawing").size(500,500);
var rect = draw.rect(50,50);
rect.animate().curvedmove(100,100);
The two functions I made:
function animateJump(object,start,end,ampl,y,i=0){
var speed = 25;
var pos = 0;
pos = start+i*((end-start)/speed);
object.animate(1).move(pos,y+bounceFunction(start,end,ampl,pos));
if (i <= speed){
animateJump(object,start,end,ampl,y,i+1)
}
}
function bounceFunction(a,b,c,x){
return -1 * (x-a)*(x-b) * c * (4/((a-b)*(b-a)));
}
Is there some easy solution?
Thanks for any help!
The animate method establish a new animation context in which runs the timer you specified (1 sec by default). So if you do el.animate().move(100,100) the element will move to position 100,100 in 1 second.
However, if you want to use your own function you need to listen to the during event which gives you the current position from 0-1 in time.
el.animate().during(function(pos, morphFn, easedPos) {
this.move(pos, bounceFunction(pos))
})
Note that pos is a value between 0 and 1 so setting it directly as coordinate does not make that much sense. You need to figure our the start and end value of the move and calculate it yourself (or use the morphFn like morphFn(start, end))
Example:
var startX = 100
var endX = 300
var startY = 100
var endY = 300
el.animate().during(function(pos, morphFn, easedPos) {
var x = morphFn(startX, endX)
var y = SVG.morph(bounceFunction(pos))(startY, endY)
this.move(x, y)
})
the morphFn is by default bound to the current position. So if you have your own position (like when using your custom bounce function) you need to create a new morph function which you can do with the SVG.morph method. This method expects a position and gives back a morph function bound to this positon.
So this would be the same:
var x = SVG.Morph(pos)(startX, endX)
var y = SVG.Morph(bounceFunction(pos))(startY, endY)

Using circle math for solid collision

function distance(x1, y1, x2, y2) {
var x = x1 - x2;
var y = y1 - y2;
return(Math.sqrt((x*x) + (y*y)))
};
function collisionCirc(circ1, circ2) {
var d = distance(circ1.x, circ1.y, circ2.x, circ2.y);
var r = circ1.radius + circ2.radius;
return(r > d);
};
function collisionCircPoint(circ1, circ2) {
var cx = ((circ1.x * circ2.radius) + (circ2.x * circ1.radius)) / (circ1.radius + circ2.radius);
var cy = ((circ1.y * circ2.radius) + (circ2.y * circ1.radius)) / (circ1.radius + circ2.radius);
var p = [cx, cy];
return p;
};
function angleDegrees(x1, y1, x2, y2) {
return (Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI) + 180;
};
function updateCollisions() {
var a;
var p;
Player.hitArea = new PIXI.Circle(Player.sprite.x, Player.sprite.y, 20);
MapObjects.chest.hitArea = new PIXI.Circle(MapObjects.chest.x, MapObjects.chest.y, 20);
if (collisionCirc(Player.hitArea, MapObjects.chest.hitArea)) {
a = angleDegrees(Player.sprite.x, Player.sprite.y, MapObjects.chest.x, MapObjects.chest.y);
p = collisionCircPoint(Player.hitArea, MapObjects.chest.hitArea);
Player.sprite.x = p[0];
Player.sprite.y = p[1];
};
};
I have 2 sprites on the map and each has a circle hitArea defined. I am trying to make a smooth circular collision that the player cannot pass through. I thought I could just set the Player.sprite's coordinates to the point of collision but it just warps him to the MapObjects.chest's coordinates, even though the point of collision is correct and is 20 pixels from the MapObject.chest's center. What am I doing wrong or what more information is needed to create a collision much like the JavaScript physics libraries where I can circle around a circle object?
The collision point is between the player and the obstacle. If you move the player towards the collision point, you are actually moving the player closer. For example, if there's exactly 40 px (r1+r2) between the player and the obstacle, the collision point is between them, at only 20 px from the obstacle!
When you have multiple objects, getting it right when the collision has already happened is difficult. If there is only one obstacle nearby, you can simply move the player directly away from the obstacle. However, this way the player might actually end up inside another obstacle.
Another solution is to go back to the start and try smaller movements, until there is no collision. This way you would eventually get it right, but this might also be slow.
The mathematically correct solution is to calculate the maximum distance to move before the collision happens. This is done by solving the following vector equation:
# p = player position before moving
# o = obstacle position
# u = player direction (unit vector)
# d = distance to move
distance(o, p + d * u) = o.radius + p.radius
That's mathematics, you may solve it by yourself or using a tool like Wolfram Alpha.
Solving this equation will give you zero, one or two possible values for the distance. Negative values you can dismiss, as they mean that the player is already past the obstacle. If you get only one value, it means that the player would merely brush the obstacle, which you can also dismiss. Two values mean that the collision happens between these distances; the smaller value is where the collision starts, and the larger value is where the player would already be through the obstacle. Also, if one value is positive and the other is negative, it means that the player is already inside the obstacle, which should never happen.
You should run this check for all nearby obstacles and then move the player according to the smallest non-negative result (that is, zero or positive), or less, if the player can't move that fast.
Finally, to circle around a round object, you can move the player a little bit in a perpendicular direction (either left or right, depending on which side of the obstacle the player will be passing) after a collision, if this doesn't cause any new collisions.
There are many other possible implementations.
Player.hitArea = new PIXI.Circle(Player.sprite.x, Player.sprite.y, 20);
MapObjects.chest.hitArea = new PIXI.Circle(MapObjects.chest.x, MapObjects.chest.y, 20);
if (collisionCirc(Player.hitArea, MapObjects.chest.hitArea)) {
p = collisionCircPoint(Player.hitArea, MapObjects.chest.hitArea);
a = angleDegrees(Player.sprite.x, Player.sprite.y, MapObjects.chest.x, MapObjects.chest.y);
if (Player.sprite.x - MapObjects.chest.x > 0) {
Player.sprite.x += 1;
} else if (Player.sprite.x + MapObjects.chest.x > 0) {
Player.sprite.x -= 1;
};
if (Player.sprite.y - MapObjects.chest.y > 0) {
Player.sprite.y += 1;
} else if (Player.sprite.y + MapObjects.chest.y > 0) {
Player.sprite.y -= 1;
};
};
};
I added that and it actually works well enough minus the player speed being slightly too fast when running into the MapObjects.chest's hitArea at certain angles. Work on that later.

Easeljs snap to grid

I am building an app using HTML5 in which a grid is drawn. I have some shapes on it that you can move.
What I'm trying to do is to snap objects to some points defined when you hover them while moving a shape.
What I tried is to save anchors points inside an array and when the shape is dropped, I draw the shape on the closest anchor point.
Easeljs is my main js lib so I want to keep it but if needed I can use an other one with easeljs.
Thanks in advance for your help!
This is pretty straightforward:
Loop over each point, and get the distance to the mouse
If the item is closer than the others, set the object to its position
Otherwise snap to the mouse instead
Here is a quick sample with the latest EaselJS: http://jsfiddle.net/lannymcnie/qk1gs3xt/
The distance check looks like this:
// Determine the distance from the mouse position to the point
var diffX = Math.abs(event.stageX - p.x);
var diffY = Math.abs(event.stageY - p.y);
var d = Math.sqrt(diffX*diffX + diffY*diffY);
// If the current point is closeEnough and the closest (so far)
// Then choose it to snap to.
var closest = (d<snapDistance && (dist == null || d < dist));
if (closest) {
neighbour = p;
}
And the snap is super simple:
// If there is a close neighbour, snap to it.
if (neighbour) {
s.x = neighbour.x;
s.y = neighbour.y;
// Otherwise snap to the mouse
} else {
s.x = event.stageX;
s.y = event.stageY;
}
Hope that helps!

Categories

Resources