I'm making a battleship simulator that controls a battleship with the WASD keys and the turret with the mouse pointer. The turret can move 360 degrees.
It rotates as it should; however, whenever the mouse pointer makes the turret reach an angle of 0 or 360 degrees, it begins rotating endlessly until I move the mouse pointer back to a different angle.
Attached is the code I have so far for turret movement:
var PTurret1angle:Number = 270;
function PTurretRotate(Evt:Event){
var Turret1x:Number;
var Turret1y:Number;
var Turret1Angle:Number;
Turret1x = mvi_PTurret1.x - mouseX;
Turret1y = mvi_PTurret1.y - mouseY;
Turret1Angle = Math.round(Math.atan2(Turret1y,Turret1x) * (180/Math.PI) + 180);
if(Turret1Angle > PTurret1angle){
mvi_PTurret1.rotation += 1;
PTurret1angle += 1;
if(PTurret1angle == 360){
PTurret1angle = 0;
}
}
else if(Turret1Angle < PTurret1angle){
mvi_PTurret1.rotation -= 1;
PTurret1angle -= 1;
if(PTurret1angle == 0){
PTurret1angle = 360;
}
}
txt_Turret1Angle.text = Turret1Angle.toString();
txt_PTurret.text = PTurret1angle.toString();
}
So, my two questions are:
1) How do I ensure that the turret will remain locked on to where the mouse pointer is, regardless of mouse pointer position?
2) Is there any way to make the rotation more efficient? For example, if my pointer requires the turret to only turn about 30 degrees, it will actually turn 330 degrees depending on the circumstance.
Thank you for your help.
Your turret angles are funky because of these two if statements:
if(PTurret1angle == 0){
PTurret1angle = 360;
}
and
if(PTurret1angle == 360){
PTurret1angle = 0;
}
These are making your endless rotation (mouse on top, angle is zero, angle is set to 360 which is greater than zero, you subtract one, oh man now it's at 359 which is greater than zero, gotta rotate all the way around, oh man we got to zero, gotta set it to 360, ......etc.....).
You can accomplish the "efficient rotation" by checking the difference between Turret1Angle and PTurret1angle. Here are my assumptions:
Zero is pointing straight up.
Positive rotation is clockwise rotation.
Turret1Angle is mouse angle and PTurret1angle is actual current turret angle
The most you ever want to turn is 180 degrees. (efficient rotation)
That being said, you can figure out which way to turn based on two things: the sign of the difference between Turret1Angle (mouse angle) and PTurret1angle (actual turret angle), and the magnitude of this difference. What do I mean by this?
examples using Turret1Angle (mouse) - PTurret1angle (actual):
Mouse is at 1 degree, turret is at 359. Mouse - turret = -358. The most you want to turn is 180 degrees, so since abs(-358) > 180, then add 360 (as the difference is negative). This gives you +2, which means turn 2 degrees clockwise!
Mouse is at 359, turret is at 1. Mouse - turret = 358. abs(358) > 180, so subtract 360 degrees (as the difference is positive). This gives you -2, so turn 2 degrees counterclockwise!
Mouse is at 1, turret is at 3. Mouse - turret = -2. Since abs(-2) < 180, we don't need to add 360, just turn 2 degrees counterclockwise!
I'm a little rusty with my ActionScript, so I won't actually code this out for you. I think I've explained how to implement the two fixes you asked about thoroughly, but if you have any trouble I'd be happy to psuedocode it.
EDIT: Here's some psuedocode:
var AngleDiff:Number;
// starting after declarations/getting basic information, within your function
Turret1Angle = Math.round(Math.atan2(Turret1y,Turret1x) * (180/Math.PI) + 180);
AngleDiff = Turret1Angle - PTurret1angle;
if(Math.abs(AngleDiff) > 180){ // efficient movement
if(AngleDiff > 0){
AngleDiff -= 360;
}else if(AngleDiff < 0){
AngleDiff += 360;
}
if(AngleDiff > 0){ // do the actual movement
PTurret1Angle += 1;
mvi_PTurret1.rotation += 1;
}else if(AngleDiff < 0){
PTurret1Angle -= 1;
mvi_PTurret1.rotation -= 1;
}
You can probably fix the numbers greater than 360 problem with modulo division, as Lukasz suggests.
PTurret1Angle = PTurret1Angle % 360;
(note: that was more actual code than psuedocode. I haven't actually tested it though so it may/may not work)
Related
I have an object called player and a virtual joystick controller called joystick and it contains its knob called knob and I've placed the joystick and the knob in a different movieclip.
I use a script to move the player as the amount of movement of the knob.
Here's the code :
function enterFrame(e:Event): void {
if (moving) {
// I move the knob as the touch point moves
// and the player as the knob moves
} else {
// I'm trying to slow down the knob movement to get back to its default position
// and slow down the player movement until it stopped
if (knob.x - joystick.x > 0) {
knob.x -= (knob.x - joystick.x)*.5;
player.x -= -(knob.x - joystick.x)*.9;
}
if (knob.x - joystick.x < 0) {
knob.x += -((knob.x - joystick.x)*.5);
player.x += (knob.x - joystick.x)*.9;
}
if (knob.y - joystick.y > 0) {
knob.y -= (knob.y - joystick.y)*.5;
player.y -= -((knob.y - joystick.y)*.9);
}
if (knob.y - joystick.y < 0) {
knob.y += -((knob.y - joystick.y)*.5);
player.y += (knob.y - joystick.y)*.9;
}
// Then I use trace to trace the player x position
trace(player.x);
}
}
And what I got is when I move the knob to left position and touch_end it, the slow down works but the player keep moving left by .01. But when I move the knob to another directions and touch_end it, the slow down works well until the player stopped.
Why could that happens?
Is there a way I can fix this?
This looks like a floating-point precision effect. If you start with knob.x at 1 and joystick.x at 0.5, you'll get:
knob.x joystick.x knob-joystick knob-joystick/2
1 0.5 0.5 0.25
0.75 0.5 0.25 0.125
0.625 0.5 0.125 0.0625
0.5625 0.5 0.0625 0.03125
0.53125 0.5 0.03125 0.015625
0.515625 0.5 0.015625 0.0078125
It's clearly trending towards zero, but there's a small remaining positive value when you calculate knob.x-joystick.x.
Note: Since your if statement that changes knob.x is in an EnterFrame listener, any joystick changes in response to user input will take longer than this to occur.
Instead of checking that your difference is > 0, check that it's greater than some small guard value, e.g.
if (knob.x - joystick.x > 0.01) {
knob.x -= (knob.x - joystick.x)*.5;
player.x -= -(knob.x - joystick.x)*.9;
}
//etc.
You might also add some guard code at the end of your listener:
if (knob.x > 0 && knob.x < 0.01) {
knob.x = 0;
}
//etc.
Well, after working around it, finally I did it!
I use Math.floor "trick" to force the -0.0249999999999828 (the last position when moving to left which make the player keep moving left even when I'm not control it anymore) to be change to 0.
Here's the code :
function enterFrame(e:Event): void {
.... // bla bla bla
} else {
if (knob.x > joystick.x) {
knob.x -= (knob.x - joystick.x)*.5;
player.x -= -((knob.x - joystick.x)*.9);
}
if (knob.x < joystick.x) {
knob.x += -(Math.floor((knob.x - joystick.x)*.5));
player.x += Math.floor((knob.x - joystick.x)*.9);
}
if (knob.y > joystick.y) {
knob.y -= (knob.y - joystick.y)*.5;
player.y -= -((knob.y - joystick.y)*.9);
}
if (knob.y < joystick.y) {
knob.y += -(Math.floor((knob.y - joystick.y)*.5));
player.y += Math.floor((knob.y - joystick.y)*.9);
}
}
}
I changed the if (knob.x - joystick.x > 0) (and all of kind of this) to if (knob.x > joystick.x) I think it's more efficient to detect where the knob is (right, left, up, down).
Or maybe you guys can find another way more efficient and more exact? It would help me
I have a gun and a player. I want to constrain the angle of the gun so the player doesn't raise his gun up too much or too low. The player and gun turns to the right side when the mouse is facing the right, and if the mouse faces the left, the player and gun turns to the left. I want to constrain the gun between 160 and -160 when it faces right. And constrain the gun between 20 and -20 when it faces left. So it doesn't rotate over the constraining restriction.
I have a code that makes it rotate 360, but I'm not sure on how to stop it from rotating once it reaches a certain point.
if (parent != null)
{
var dx = MovieClip(parent).crosshair.x - x;
var dy = MovieClip(parent).crosshair.y - y;
var angle = Math.atan2(dy, dx)/Math.PI * 180;
rotation = angle;
}
Ok, here's what's happening:
Flash rotation at 0° faces strictly up. So the right side will be from 0° to 180° and left side will be from 0° to -180°. This is easy to separate because all right side > 0 and all left side < 0.
But, Math.atan2(dy, dx) calculates a different angle which cannot be directly assigned to object rotation. Instead of left and right side, it is < 0 on upper side and > 0 on lower side. If you calculate the rotation that way, it will be a mess.
So, the atan calculation must be shifted by 90° clockwise in order to match the Flash rotation. This is done by transforming the parameters and now it looks like Math.atan2(dx, -dy). After that the calculated angle and the rotation angle will match.
var angle:Number = Math.atan2(dx, -dy) / Math.PI * 180;
if (angle < 0) { // facing left
if (angle > -30) angle = -30;
if (angle < -150) angle = -150;
} else { // facing right
if (angle < 30) angle = 30;
if (angle > 150) angle = 150;
}
This is the solution without using -dy and instead using dy. (Code added by OP and I didn't check it :)
var angle = Math.atan2(dy, dx) / Math.PI * 180;
if (rotation > -180 && rotation < -90 || rotation > 90 && rotation < 180 )
{ // facing left
if (rotation > -150 && rotation < 0)
{
rotation = -150;
}
if (rotation < 120 && rotation > 0)
{
rotation = 120;
}
}
else
{
{ // facing right
if (rotation < -30)
{
rotation = -30;
}
if (rotation > 60)
{
rotation = 60;
}
}
So I have been searching for a way to have a background orbit around a centerpoint. I came across the greensock blitmask that does an amazing job of wrapping the bitmap data to do infinte scrolling effects. However, I can't figure out a way to use this blitmask to rotate the bitmap data and still have the wrapping effect. Below is a link to my SWF.
The image that moves is the one that I wish to wrap and have the infinite scrolling effect. The problem is dealing with repositioning after the image has moved off the screen since it has been rotated.
EDIT: I totally forgot about this issue and decided put it on the backburner for my game since it was taking too long to figure. I recently returned to this concept because I had an idea to make it work. Below is a link to the .SWF that shows what I was trying to accomplish. Though this example works, I dont feel its the best solution.
"WASD" control movement
Orbiting Background
I used some trigonometry to calculate the distance a star is from the player. If that star is beyond that distance, reposition it using it's angle * -1. The code for this is under the link.
var travelVal:Number = 0;
var turnVal:Number = 0;
var currentChild:DisplayObject;
var currentStar:Star;
var childIndex:int = 0;
var angle:Number = 0;
var distance:Number = 0;
if (controller.isKeyDown(Keyboard.A))
{
turnVal += TURN_SPEED;
}
if (controller.isKeyDown(Keyboard.D))
{
turnVal -= TURN_SPEED;
}
if (controller.isKeyDown(Keyboard.W))
{
travelVal += PLAYER_SPEED;
}
if (controller.isKeyDown(Keyboard.S))
{
travelVal -= PLAYER_SPEED
}
for (childIndex = 0; childIndex < numChildren; childIndex++)
{
currentChild = getChildAt(childIndex);
//if (currentChild != player && currentChild != debugOutput && currentChild != blackBkgd)
if(currentChild is Star)
{
currentStar = currentChild as Star;
//move this child based on the travel value
currentChild.y += travelVal * currentStar.speed;
//calculate the orbiting
distance = Math2.distanceBetweenObjects(player, currentChild);
angle = Math.atan2(currentChild.y - player.y, currentChild.x - player.x);
if (distance > STAGE_WIDTH ) angle = angle * -1;
//get orginal angle in radians
//angle = Math.atan2(currentChild.y - player.y , currentChild.x - player.x);
angle = Math2.radiansToDegress(angle);
angle += turnVal;
//currentStar.rotation = angle;
angle = Math2.degreesToRadians(angle);
currentChild.x = player.x + (distance * Math.cos(angle));
currentChild.y = player.y + (distance * Math.sin(angle));
}
}
In order to rotate around a certain centerpoint, you first translate by (-centerpoint.x,-centerpoint.y), then rotate around (0,0) and then translate back by (centerpoint.x,centerpoint.y).
I'd like to move an AS 3 movieclip randomly. This is what I currently have, bound to the ENTER_FRAME event. This obviously moves the movieclip from the left upper to the right lower edge, so I need some kind of switch to add/substract the target positions.
function movePsycho(e:Event):void {
e.target.y += Math.random()*2;
e.target.x += Math.random()*2;
if (e.target.y >= stage.height || e.target.x >= stage.width)
e.target.removeEventListener(Event.ENTER_FRAME, movePsycho);
}
You don't need add/substract thing. You just have to make sure not only you get positive values out of your random, but negatives too, so it runs to all sides.
Try changing your random generating lines to this:
e.target.y += Math.random()*10 - 5;
e.target.x += Math.random()*10 - 5;
This will work if you want to make it move in a 5px radius.
I just realized you may want to generate a new random point on the screen, then move to that point and when your object reaches the destination generate another random point to go to. So if that's the case, try this:
mc.addEventListener(Event.ENTER_FRAME, onFrame);
var dirX:int = mc.x;
var dirY:int = mc.y;
function generateRandomPoint():void
{
dirX = Math.random() * stage.stageWidth;
dirY = Math.random() * stage.stageHeight;
}
function onFrame(e:Event):void
{
mc.x += (dirX - mc.x) * 0.1;
mc.y += (dirY - mc.y) * 0.1;
if(Math.abs(dirX - mc.x) < 1 || Math.abs(dirY - mc.y) < 1)
generateRandomPoint();
}
i don't know actionscript but you may find help with this
http://www.actionscript.org/forums/showthread.php3?t=270725
I have this problem: there is this enemy which rotates to my player. While "orbiting" the enemy with my player I can see that the enemy is rotating towards my player.
And then the enemy suddenly turns around 360 degrees and facing to my player again. I don't know why it does this strange 360 degree turn but it happens every time when I orbit the enemy for a few seconds. I don't know where the problem might be.
tempEnemy.dX = tempEnemy.x - player.x;
tempEnemy.dY = tempEnemy.y - player.y;
tempEnemy.rotateTo = toDegrees(getRadians(tempEnemy.dX, tempEnemy.dY));
if(tempEnemy.frame < 0) tempEnemy.frame += 360;
if(tempEnemy.frame > 359) tempEnemy.frame -= 360;
tempEnemy.trueRotation = int((tempEnemy.rotateTo - tempEnemy.frame) / tempEnemy.rotateSpeed);
tempEnemy.vX += (player.x - tempEnemy.x) / tempEnemy._speed;
tempEnemy.vY += (player.y - tempEnemy.y) / tempEnemy._speed;
tempEnemy.vX *= tempEnemy.decay;
tempEnemy.vY *= tempEnemy.decay;
Update:
private function toDegrees(radians:Number):Number
{
var degrees:Number = Math.floor(radians * 180 / Math.PI);
//trace (degrees);
return degrees;
}
private function getRadians(deltaX:Number, deltaY:Number):Number
{
var radian:Number = Math.atan2(deltaY, deltaX);
if (deltaY < 0)
{
radian += (2 * Math.PI);
}
return(radian);
}
Without seeing your getRadians function, perhaps you're passing 360° calculating a reflex angle?
Maybe something like:
var degrees:Number = Math.atan2(tempEnemy.y - player.y, tempEnemy.x - player.x) * 180 / Math.PI;
var delta:Number = degrees - tempEnemy.rotation;
while (delta <= -180)
delta += 360;
while (delta > 180)
delta -= 360;
Compensate degrees with the resting angle of your enemy display object design.
It sounds like you've used a timeline tween. Either use a code tween, or grab the tween I used here http://flexdiary.blogspot.com/2010/04/sample-code-for-oop-timeline-insideria.html. I don't remember what exactly I did to make the timeline tween go full circle, rather than twisting back before restarting, but you should be able to export and reuse the tween.
If you want to use a code tween, you can either use Flash's built in tween classes or use a library like Tweensy.