I am attempting to create a Camera that moves with the Player but locks onto an enemy when the player clicks the lock on button. The behaviour is almost working as I want it, the camera locks onto the target. And when the player stand in-front of the target it works fine. However as soon as the player runs past the target, the camera behaves strangely. It still looks at the Enemy, however it does not stay behind the player. Here is the code that dictates the behaviour:
if(MouseLock.MouseLocked && !lockedOn){ // MOUSE CONTROL:
Data.Azimuth += Input.GetAxis("Mouse X") * OrbitSpeed.x;
Data.Zenith += Input.GetAxis("Mouse Y") * OrbitSpeed.y;
} else if(lockedOn) { // LOCKON BEHAVIOUR:
FindClosestEnemy();
}
if (Target != null) {
lookAt += Target.transform.position;
base.Update ();
gameObject.transform.position += lookAt;
if(!lockedOn){
gameObject.transform.LookAt (lookAt);
} else if(enemyTarget != null) {
Vector3 pos1 = Target.transform.position ;
Vector3 pos2 = enemyTarget.transform.position ;
Vector3 dir = (pos2 - pos1).normalized ;
Vector3 perpDir = Vector3.Cross(dir, Vector3.right) ;
Vector3 midPoint = (pos1 + pos2) / 2f;
gameObject.transform.LookAt (midPoint);
}
}
And the Code for Finding the nearest Enemy:
void FindClosestEnemy () {
int numEnemies = 0;
var hitColliders = Physics.OverlapSphere(transform.position, lockOnRange);
foreach (var hit in hitColliders) {
if (!hit || hit.gameObject == this.gameObject || hit.gameObject.tag == this.gameObject.tag){
continue;
}
if(hit.tag != "Enemy") // IF NOT AN ENEMY: DONT LOCK ON
continue;
var relativePoint = Camera.main.transform.InverseTransformPoint(hit.transform.position);
if(relativePoint.z < 0){
continue;
}
numEnemies += 1;
if(enemyTarget == null){
print ("TARGET FOUND");
enemyTarget = hit;
}
}
if(numEnemies < 1){
lockedOn = false;
enemyTarget = null;
}
}
As I said, teh behaviour almost works as expected, however I need the camera to stay behind the player whilst locked on and it must face the enemy/midPoint between the enemy and player. How can this be done? Thank you for your time.
To clarify your intent: you want to lock the position relative to the target (player), whilst setting the camera rotation to look at either the target or a secondary target (enemy)? And your current code performs the rotation correctly but the positioning is buggy?
The easiest way to fix the camera relative to another object is to parent it in the scene. In your case you could add the camera as a child under the Player game object.
If you would rather not do this then look at your positioning code again:
lookAt += Target.transform.position;
base.Update ();
gameObject.transform.position += lookAt;
I don't know where lookAt comes from originally but to me this looks all wrong. Something called lookAt should have nothing to do with position and I doubt you want to += anything in the positioning code given that you want a fixed relative position. Try this instead:
public float followDistance; // class instance variable = distance back from target
public float followHeight; // class instance variable = camera height
...
if (Target != null) {
Vector3 newPos = target.position + (-Target.transform.forward * followDistance);
newPos.y += followHeight;
transform.position = newPos;
}
This should fix the positioning. Set the followDistance and followHeight to whatever you desire. Assuming your rotation code works this should fix the problem.
Related
I'm trying to make a homing projectile for my bullet hell game and I'd need to be able to calculate the angle between the target and projectile relatively to the projectile's angle (0 degrees would be the direction the projectile is pointing). Right now the angle calculation is absolute done using point_direction, but the problem is when the target is at the 4th sector the projectile starts steering the wrong way. Another issue is that if the projectile does a 180 degree turn while chasing the target (or moves down if fired by enemy) the steering direction will get inverted. I have also tried mp_potential_ functions but their pathfinding is too "agressive".
This is what my current code looks like:
if(instance_exists(obj_fighter1)) {
var target;
target = instance_nearest(x, y, obj_fighter1);
if(target != noone) {
var angle_to_target;
angle_to_target = point_direction(x,y,target.x,target.y);
if(angle_to_target < direction) {
direction -= 2;
}
if(angle_to_target > direction) {
direction += 2;
}
}
}
Hopefully this information is enough and is understandable.
Okay, a common Game Maker question. The routine I use is below. Looking at it, it could do with a bit of refactoring, but it does work.
var wantDir;
var currDir;
var directiondiff;
var maxTurn;
// want - this is your target direction \\
wantDir = argument0;
// max turn - this is the max number of degrees to turn \\
maxTurn = argument1;
// current - this is your current direction \\
currDir = direction;
if (wantDir >= (currDir + 180))
{
currDir += 360;
}
else
{
if (wantDir < (currDir - 180))
{
wantDir += 360;
}
}
directiondiff = wantDir - currDir;
if (directiondiff < -maxTurn)
{
directiondiff = -maxTurn
}
if (directiondiff > maxTurn)
{
directiondiff = maxTurn
}
return directiondiff
So you'd call this, and it'll return you a value that you can add to your missile's angle. So if you call it scr_get_angle, your code might then look like this:
if(instance_exists(obj_fighter1)) {
var target;
target = instance_nearest(x, y, obj_fighter1);
if(target != noone) {
var angle_to_target;
angle_to_target = point_direction(x,y,target.x,target.y);
direction += scr_get_angle(angle_to_target, 2);
}
}
I'm trying to program a network card game with javafx but I have a problem.
I have programmed an own class CardImageView that extends from ImageView so that I can save the Card-Object there.
I'm able to add this CardImageView to my main group on my pc and on my "enemy's" pc and I am able to remove it on my pc.
But when I try to remove it at my "enemy's" pc it is still visible (but not "interactable".
For network connectivity I am using a Task that gives me the commands as Strings over the message property.
private void onCardRemoved(String msg) {
System.out.println("CARD REMOVED BY: "+Thread.currentThread());
String[] params = msg.split(";");
//Calculating the coordinates of the card on screen
double x = getWidth() - (Double.parseDouble(params[1])/100*getWidth()+card_width/2);
double y = getHeight() - (Double.parseDouble(params[2])/100*getHeight()+card_height/2);
CardImageView toRemove = null;
System.out.println("To-Remove ("+x+"/"+y+")");
//searching for the card in my group
for(Node n : spielfeld.getChildren()) {
if(n instanceof CardImageView && n.getLayoutX() == x && n.getLayoutY() == y) {
toRemove = (CardImageView) n;
break;
}
}
//removing the card
if(toRemove != null) spielfeld.getChildren().remove(toRemove);
}
I´m newbie in Unity. I want rotate my 2D object based on user touch moved (moved finger on the screen). I have this code:
void Update ()
{
if (Input.touches.Length > 0) {
t = Input.GetTouch (0);
if (t.phase == TouchPhase.Moved) {
Vector3 movePos = new Vector3 (t.position.x, t.position.y, 0);
var objectPos = Camera.main.WorldToScreenPoint (transform.position);
var dir = movePos - objectPos;
transform.rotation = Quaternion.Euler (new Vector3 (0f, 0f, Mathf.Atan2 (dir.y, dir.x) * Mathf.Rad2Deg));
}
}
}
This code rotate the object based on user touch but when I touch screen again in another position and do touch move, it will rotate the whole object to the actual touch and then it will do correct object rotation based on touch move.
And I dont´t want rotate the whole object based on touch position but rotate the object only based on touch move. Do you understand me? Can you help me? How should I rewrite my code?
If I understand you, try to use this code below:
private float turnSpeed = 5f;
private Vector2 movement;
void Update()
{
Vector2 currentPosition = transform.position;
if (Input.touchCount > 0)
{
Touch touch = Input.GetTouch(0);
if (touch.phase == TouchPhase.Moved)
{
Vector2 moveTowards = Camera.main.ScreenToWorldPoint(touch.position);
movement = moveTowards - currentPosition;
movement.Normalize();
}
}
float targetAngle = Mathf.Atan2(movement.y, movement.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.Euler(0, 0, targetAngle), turnSpeed * Time.deltaTime);
}
Let me know if is what you want. Also, there is a complete sample here: https://github.com/joaokucera/unity-2d-object-rotation
Look into using deltaPosition instead of position on your touch. That should get you in the right direction.
var movedVector = t.deltaPosition;
Edit:
Here is a possible integration with your existing code. I don't have Unity on this PC so this is entirely untested. The main idea is you are getting a change in the finger position between frames. You then scale that change by move speed, and of course, the change in time between frame renders (delta time).
How the object rotates relative to that information is up to you. I just inserted the logic into your existing code.
float moveSpeed = 2.0f;
void Update ()
{
if (Input.touches.Length > 0) {
t = Input.GetTouch (0);
if (t.phase == TouchPhase.Moved) {
var delta = t.deltaPosition * moveSpeed * Time.deltaTime;
transform.rotation = Quaternion.Euler (new Vector3 (0f, 0f, Mathf.Atan2 (delta .y, delta.x) * Mathf.Rad2Deg));
}
}
}
I have added UIPinchGestureRecognizer to my scene.view to scale my content. I actually scale the parent node where all my visible contents reside. But I have problem though with scaling point. The thing is node scale from the lower-left corner. It's definitely not what I want. Do I have to write lots of code to be able to scale from the point where pinching occurs? Could you please give some hints as to what way to follow.
I have been working on the same problem and my solution is shown below. Not sure if it is the best way to do it, but so far it seems to work. I'm using this code to zoom in and out of an SKNode that has several SKSpriteNode children. The children all move and scale with the SKNode as desired. The anchor point for the scaling is the location of the pinch gesture. The parent SKScene and other SKNodes in the scene are not affected. All of the work takes place during recognizer.state == UIGestureRecognizerStateChanged.
// instance variables of MyScene.
SKNode *_mySkNode;
UIPinchGestureRecognizer *_pinchGestureRecognizer;
- (void)didMoveToView:(SKView *)view
{
_pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:#selector(handleZoomFrom:)];
[[self view] addGestureRecognizer:_pinchGestureRecognizer];
}
// Method that is called by my UIPinchGestureRecognizer.
- (void)handleZoomFrom:(UIPinchGestureRecognizer *)recognizer
{
CGPoint anchorPoint = [recognizer locationInView:recognizer.view];
anchorPoint = [self convertPointFromView:anchorPoint];
if (recognizer.state == UIGestureRecognizerStateBegan) {
// No code needed for zooming...
} else if (recognizer.state == UIGestureRecognizerStateChanged) {
CGPoint anchorPointInMySkNode = [_mySkNode convertPoint:anchorPoint fromNode:self];
[_mySkNode setScale:(_mySkNode.xScale * recognizer.scale)];
CGPoint mySkNodeAnchorPointInScene = [self convertPoint:anchorPointInMySkNode fromNode:_mySkNode];
CGPoint translationOfAnchorInScene = CGPointSubtract(anchorPoint, mySkNodeAnchorPointInScene);
_mySkNode.position = CGPointAdd(_mySkNode.position, translationOfAnchorInScene);
recognizer.scale = 1.0;
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
// No code needed here for zooming...
}
}
The following are helper functions that were used above. They are from the Ray Wenderlich book on Sprite Kit.
SKT_INLINE CGPoint CGPointAdd(CGPoint point1, CGPoint point2) {
return CGPointMake(point1.x + point2.x, point1.y + point2.y);
}
SKT_INLINE CGPoint CGPointSubtract(CGPoint point1, CGPoint point2) {
return CGPointMake(point1.x - point2.x, point1.y - point2.y);
}
SKT_INLINE GLKVector2 GLKVector2FromCGPoint(CGPoint point) {
return GLKVector2Make(point.x, point.y);
}
SKT_INLINE CGPoint CGPointFromGLKVector2(GLKVector2 vector) {
return CGPointMake(vector.x, vector.y);
}
SKT_INLINE CGPoint CGPointMultiplyScalar(CGPoint point, CGFloat value) {
return CGPointFromGLKVector2(GLKVector2MultiplyScalar(GLKVector2FromCGPoint(point), value));
}
I have translated ninefifteen's solution for Swift and Pinch Gestures. I spent a couple days trying to get this to work on my own. Thank goodness for ninefifteen's Obj-C post! Here is the Swift version that appears to be working for me.
func scaleExperiment(_ sender: UIPinchGestureRecognizer) {
var anchorPoint = sender.location(in: sender.view)
anchorPoint = self.convertPoint(fromView: anchorPoint)
let anchorPointInMySkNode = _mySkNode.convert(anchorPoint, from: self)
_mySkNode.setScale(_mySkNode.xScale * sender.scale)
let mySkNodeAnchorPointInScene = self.convert(anchorPointInMySkNode, from: _mySkNode)
let translationOfAnchorInScene = (x: anchorPoint.x - mySkNodeAnchorPointInScene.x, y: anchorPoint.y - mySkNodeAnchorPointInScene.y)
_mySkNode.position = CGPoint(x: _mySkNode.position.x + translationOfAnchorInScene.x, y: _mySkNode.position.y + translationOfAnchorInScene.y)
sender.scale = 1.0
}
Can't zoom I don't know why but the main problem is those SKT_INLINE. I've googled them and didn't found anything about 'em... The problem is when I copy/paste them in my project the compiler tells me I have to add an ";" right after them. I wonder if that's the reason that I can zoom.
In Swift 4, my SKScene adds the UIPinchGestureRecognizer to the view but passes handling of the pinch gesture off to one of its SKNode children that is created in the scene's init(), due to some reasons not relevant here. Anyhow, this is ninefifteen's answer from the perspective of what s/he calls _mySkNode. It also includes a little code to limit the zoom and does not use the convenience functions listed at the bottom of his post. The #objc part of the declaration allows the function to be used in #selector().
Here is what is in my SKScene:
override func didMove(to view: SKView) {
let pinchRecognizer: UIPinchGestureRecognizer = UIPinchGestureRecognizer(target: self.grid, action: #selector(self.grid.pinchZoomGrid))
self.view!.addGestureRecognizer(pinchRecognizer)
}
And this is the relevant section in my SKNode:
// Pinch Management
#objc func pinchZoomGrid(_ recognizer: UIPinchGestureRecognizer){
var anchorPoint: CGPoint = recognizer.location(in: recognizer.view)
anchorPoint = self.scene!.convertPoint(fromView: anchorPoint)
if recognizer.state == .began {
// No zoom code
} else if recognizer.state == .changed {
let anchorPointInGrid = self.convert(anchorPoint, from: self.scene!)
// Start section that limits the zoom
if recognizer.scale < 1.0 {
if self.xScale * recognizer.scale < 0.6 {
self.setScale(0.6)
} else {
self.setScale(self.xScale * recognizer.scale)
}
} else if recognizer.scale > 1.0 {
if self.xScale * recognizer.scale > 1.5 {
self.setScale(1.5)
} else {
self.setScale(self.xScale * recognizer.scale)
}
}
// End section that limits the zoom
let gridAnchorPointInScene = self.scene!.convert(anchorPointInGrid, from: self)
let translationOfAnchorPointInScene = CGPoint(x:anchorPoint.x - gridAnchorPointInScene.x,
y:anchorPoint.y - gridAnchorPointInScene.y)
self.position = CGPoint(x:self.position.x + translationOfAnchorPointInScene.x,
y:self.position.y + translationOfAnchorPointInScene.y)
recognizer.scale = 1.0
} else if recognizer.state == .ended {
// No zoom code
}
}
I have a strange issue- I use a title window to create a message to the user. The user has the ability to move the title window around the screen so that the main screen is visible.
However if the user were to move the title window way too much, it can actually go outside the browser accessible area- the user has no option but to close the browser and start again. How do we ensure that the title window movement is limited, such that the title bar is always available for control?
I might not have worded this right- pls check the attached image.
I'd listen to the move event of the TitleWindow. If the window is moved out of the visible coordinates of the application, move it back.
If you're only issue is w/ allowing the users to closet the window, then you could add a "Close" button on the bottom of the window in addition to the 'x' at the top.
If you can use a custom component I'd suggest you override the TitleWindow's move() method. I'm using the following code to restrict the window movement:
public class PopUpWindow extends TitleWindow
{
private static const MIN_VISIBLE:int = 50;
public override function move(x:Number, y:Number):void
{
var maxX:Number = stage.stageWidth - MIN_VISIBLE;
var maxY:Number = stage.stageHeight - MIN_VISIBLE;
if (x < 0)
x = 0;
else if (x > maxX)
x = maxX;
if (y < 0)
y = 0;
else if (y > maxY)
y = maxY;
super.move(x, y);
}
}
This function is called on move event of titlewindow:
protected function titlewindow1_moveHandler(event:MoveEvent):void
{
// TODO Auto-generated method stub
var window:UIComponent = event.currentTarget as UIComponent;
var application:UIComponent = FlexGlobals.topLevelApplication as UIComponent;
var bounds:Rectangle = new Rectangle(0, 0, application.width, application.height);
var windowBounds:Rectangle = window.getBounds(application);
var x:Number;
var y:Number;
if (windowBounds.left <= bounds.left)
x = bounds.left;
else if (windowBounds.right >= bounds.right)
x = bounds.right - window.width;
else
x = window.x;
if (windowBounds.top <= bounds.top)
y = bounds.top;
else if (windowBounds.bottom >= bounds.bottom)
y = bounds.bottom - window.height;
else
y = window.y;
window.move(x, y);
}