If you use a bounding circle for the player then the "dead zone" can be defined as whenever the line-of-sight ray from the gun to the target intersects with the circle.
Detecting that the gun is intersecting the player is simple, in libGDX the Intersector.intersectSegmentCircle
method is useful for this.
What is harder is to rotate the gun so that it faces the target without ever rotating over the player.
One way of achieving this is to measure the signed angle between the vector from the gun to the target and from the gun to the center of the player.
Vector2 gunToTargetDirection = new Vector2(target).sub(gunPosition);
Vector2 gunToPlayerDirection = new Vector2(playerPosition).sub(gunPosition);
float gunToTargetAngle = signedAngleBetween(gunDirection, gunToTargetDirection);
float gunToPlayerAngle = signedAngleBetween(gunDirection, gunToPlayerDirection);
then you can compare the sign of those angles, and if they're the same and the angle to the player is smaller than the angle to the target then flip the sign of the rotation thus forcing the gun to rotate the long way round:
float gttaS = Math.signum(gunToTargetAngle);
float gtpaS = Math.signum(gunToPlayerAngle);
float direction;
if (gttaS == gtpaS) {
if (Math.abs(gunToTargetAngle) < Math.abs(gunToPlayerAngle))
direction = gttaS;
else
direction = -gttaS;
} else {
direction = gttaS;
}
gunRotation += direction * maxGunTurnSpeed * Gdx.graphics.getDeltaTime();
An example of this might look like this player is green, gun is red and target is yellow. The line inside the player and the gun indicates the direction of these and the cyan line indicates that the gun has a clear line of sight to the target and will try to rotate the gun to point at it, without overlapping with the player. The gun is always mounted on the player's left shoulder.

In this example the gun goes passive when it cannot see the target and simply follows the player's rotation, but this could easily be changed to a more interesting behavior.
The full source code for the example above is:
package com.somepackage;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
import com.badlogic.gdx.math.Intersector;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
public class SomeCoolGameWithAShoulderCannonJustLikeInThePredator extends Game {
OrthographicCamera camera;
ShapeRenderer shapeRenderer;
Vector2 playerPosition = new Vector2();
Vector2 playerDirection = new Vector2(1.0f, 0.0f);
float playerRotation = 0.0f;
float playerSpeed = 0.0f;
float playerSize = 32.0f;
float gunRotation = 0.0f;
float gunSize = 16.0f;
Vector2 gunPosition = new Vector2();
Vector2 gunDirection = new Vector2(1.0f, 0.0f);
Vector2 target = new Vector2(100.0f, 0.0f);
@Override
public void create() {
camera = new OrthographicCamera(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
shapeRenderer = new ShapeRenderer();
}
private void renderPlayer() {
shapeRenderer.setColor(Color.GREEN);
shapeRenderer.circle(playerPosition.x, playerPosition.y, playerSize, 16);
shapeRenderer.line(playerPosition.x, playerPosition.y, playerPosition.x + playerDirection.x * playerSize, playerPosition.y + playerDirection.y * playerSize);
}
private void renderGun(boolean targetIsObscuredByPlayer) {
if (!targetIsObscuredByPlayer) {
shapeRenderer.setColor(Color.CYAN);
shapeRenderer.line(gunPosition.x, gunPosition.y, target.x, target.y);
}
shapeRenderer.setColor(Color.RED);
shapeRenderer.circle(gunPosition.x, gunPosition.y, gunSize, 16);
shapeRenderer.line(gunPosition.x, gunPosition.y, gunPosition.x + gunDirection.x * gunSize, gunPosition.y + gunDirection.y * gunSize);
}
private void renderTarget() {
float targetSize = 8.0f;
shapeRenderer.setColor(Color.YELLOW);
shapeRenderer.circle(target.x, target.y, targetSize, 16);
}
private float signedAngleBetween(Vector2 a, Vector2 b) {
Vector2 na = (new Vector2(a)).nor();
Vector2 nb = (new Vector2(b)).nor();
return ((float) Math.atan2(nb.y, nb.x) - (float) Math.atan2(na.y, na.x)) * MathUtils.radiansToDegrees;
}
private float wrapAngle(float angle) {
angle = angle % 360;
if (angle < 0)
angle += 360;
return angle;
}
@Override
public void render() {
Gdx.gl.glClearColor(0, 0, 0, 0);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
float maxPlayerSpeed = 64.0f;
float playerAcceleration = 8.0f;
float maxPlayerTurnSpeed = 90.0f;
float maxGunTurnSpeed = 120.0f;
playerSpeed *= 0.95f;
float playerDeltaRotation = 0.0f;
if (Gdx.input.isKeyPressed(Input.Keys.W))
playerSpeed = Math.min(maxPlayerSpeed, playerSpeed + Gdx.graphics.getDeltaTime() * playerAcceleration);
if (Gdx.input.isKeyPressed(Input.Keys.S))
playerSpeed = Math.max(-maxPlayerSpeed, playerSpeed - Gdx.graphics.getDeltaTime() * playerAcceleration);
if (Gdx.input.isKeyPressed(Input.Keys.A))
playerDeltaRotation += Gdx.graphics.getDeltaTime() * maxPlayerTurnSpeed;
if (Gdx.input.isKeyPressed(Input.Keys.D))
playerDeltaRotation -= Gdx.graphics.getDeltaTime() * maxPlayerTurnSpeed;
playerRotation += playerDeltaRotation;
playerRotation = wrapAngle(playerRotation);
playerDirection.set(1.0f, 0.0f).rotate(playerRotation);
playerPosition.x += playerDirection.x * playerSpeed;
playerPosition.y += playerDirection.y * playerSpeed;
gunPosition.set(playerDirection).scl(playerSize + gunSize).rotate(90).add(playerPosition);
boolean targetIsObscuredByPlayer = true;
if (!Intersector.intersectSegmentCircle(gunPosition, target, playerPosition, playerSize * playerSize)) {
targetIsObscuredByPlayer = false;
Vector2 gunToTargetDirection = new Vector2(target).sub(gunPosition);
Vector2 gunToPlayerDirection = new Vector2(playerPosition).sub(gunPosition);
float gunToTargetAngle = signedAngleBetween(gunDirection, gunToTargetDirection);
float gunToPlayerAngle = signedAngleBetween(gunDirection, gunToPlayerDirection);
float gttaS = Math.signum(gunToTargetAngle);
float gtpaS = Math.signum(gunToPlayerAngle);
float direction;
if (gttaS == gtpaS) {
if (Math.abs(gunToTargetAngle) < Math.abs(gunToPlayerAngle))
direction = gttaS;
else
direction = -gttaS;
} else {
direction = gttaS;
}
gunRotation += direction * maxGunTurnSpeed * Gdx.graphics.getDeltaTime();
} else
gunRotation += playerDeltaRotation;
gunRotation = wrapAngle(gunRotation);
gunDirection.set(1.0f, 0.0f).rotate(gunRotation);
camera.update();
shapeRenderer.setProjectionMatrix(camera.combined);
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
renderPlayer();
renderGun(targetIsObscuredByPlayer);
renderTarget();
shapeRenderer.end();
}
}
Math.max(Math.min(aimAngle, 360), 90)
which works fine as is, but doesn't work at all to just add the player rotation angle... \$\endgroup\$