Spaceship Control

Hi!
I’m creating simple solar system where you can moving around using spaceship. I have problem with camera. When i’m going right or left everythink is fine, but when move my mouse to far on top or down there is a camera jump. For example when planet is on left upper corner after this ‘jump’ will be on right down corner. Do you know how to fix it?

import java.awt.Robot;
import java.awt.AWTException;
import java.awt.event.InputEvent;

PShape mars;
PShape sun;
PShape venus;
PShape earth;
PShape moon1;
PShape ship;

PImage mars_txt;
PImage sun_txt;
PImage ven_txt;
PImage earth_txt;
PImage moon1_txt;
PImage bg_txt;

float theta;
float rotX;
float rotY;
float fov = PI / 3.0; // Początkowa wartość pola widzenia

PVector shipPosition = new PVector(0, 0, 1500); // Pozycja statku
PVector shipDirection = new PVector(0, 0, -1); // Kierunek lotu statku
float shipSpeed = 5; // Prędkość statku

boolean movingForward = false;
boolean movingBackward = false;

Robot robot;

void setup() {
  size(1920, 1080, P3D);
  noCursor(); // Ukryj kursor

  try {
    robot = new Robot();
  } catch (AWTException e) {
    e.printStackTrace();
  }
  
  bg_txt = loadImage("background.jpeg");
  mars_txt = loadImage("Diffuse_2K.png");
  sun_txt = loadImage("Bump_2K.png");
  ven_txt = loadImage("venus.png");
  earth_txt = loadImage("earth.png");
  moon1_txt = loadImage("moon.png");
  
  mars = loadShape("Mars_2K.obj");
  sun = loadShape("Mars_2K.obj");
  venus = loadShape("Mars_2K.obj");
  earth = loadShape("Mars_2K.obj");
  moon1 = loadShape("Mars_2K.obj");
  ship = loadShape("Spaceship.obj");
  
  mars.setTexture(mars_txt);
  sun.setTexture(sun_txt);
  venus.setTexture(ven_txt);
  earth.setTexture(earth_txt);
  moon1.setTexture(moon1_txt);
}

void draw() {
  background(0);
  float cameraZ = (height / 2.0) / tan(fov / 2.0);
  perspective(fov, float(width) / float(height), cameraZ / 10.0, cameraZ * 10.0);
  
  // Aktualizacja pozycji statku
  if (movingForward) {
    shipPosition.add(PVector.mult(shipDirection, shipSpeed));
  }
  if (movingBackward) {
    shipPosition.sub(PVector.mult(shipDirection, shipSpeed));
  }
  
  // Ustawienie kamery za statkiem
  PVector cameraPosition = PVector.add(shipPosition, PVector.mult(shipDirection, -200)); // Kamera za statkiem
  camera(cameraPosition.x, cameraPosition.y, cameraPosition.z, 
         shipPosition.x, shipPosition.y, shipPosition.z, 
         0, 1, 0);
  print("\n" + shipPosition);
  // Transformacje świata
  pushMatrix();
  translate(shipPosition.x, shipPosition.y, shipPosition.z);
  rotateY(rotX);
  rotateX(-rotY);

    // Renderowanie statku kosmicznego
    pushMatrix(); // SPACESHIP
    rotateY(rotX);
    rotateX(rotY);
    scale(0.1);
    shape(ship);
    popMatrix();
  
  popMatrix();

  // Renderowanie innych obiektów
  pushMatrix(); // SUN
  scale(30);
  shape(sun);
  popMatrix();

  pushMatrix(); // MARS
  rotate(theta / 4);
  translate(400, 0);
  rotateY(theta / 2);
  scale(6);
  shape(venus);
  popMatrix();
  
  pushMatrix(); // VENUS
  rotate(theta / 6);
  translate(600, 0);
  rotateY(theta / 1.3);
  scale(10);
  shape(earth);
  popMatrix();
  
  pushMatrix(); // EARTH
  rotate(theta / 5);
  translate(800, 0);
  rotateY(theta / 1.3);
  scale(10);
  shape(mars);
  
    pushMatrix(); // MOON1
    rotate(theta / 1.4);
    translate(8, 0, 0);
    rotateY(theta / 1.3);
    scale(0.3);
    shape(moon1);
    popMatrix();
      
    pushMatrix(); // MOON1
    rotate(theta * 1.5);
    translate(0, -11, 0);
    rotateY(theta / 1.3);
    scale(0.3);
    shape(moon1);
    popMatrix();
      
    pushMatrix(); // MOON1
    rotate(theta);
    translate(0, 0, -13);
    rotateY(theta / 1.3);
    scale(0.3);
    shape(moon1);
    popMatrix();
  
  popMatrix();

  theta += 0.03;
  
  if (mouseX != width / 2 || mouseY != height / 2) {
    float deltaX = mouseX - width / 2;
    float deltaY = mouseY - height / 2;
    rotX -= radians(deltaX) * 0.01; // Odwrócenie ruchu lewo-prawo
    rotY -= radians(deltaY) * 0.01; // Poprawiono ruch góra-dół
    
    // Aktualizacja kierunku statku na podstawie obrotów
    shipDirection = new PVector(cos(rotY) * sin(rotX), sin(-rotY), cos(rotY) * cos(rotX)); // Poprawiono kierunek lotu
    shipDirection.normalize();
    
    // Reset pozycji kursora
    robot.mouseMove(width / 2, height / 2);
  }
}

void mouseWheel(MouseEvent event) {
  float e = event.getCount();
  fov -= e * -0.05; // Regulacja prędkości przybliżania i oddalania
  fov = constrain(fov, 0.1, PI); // Ograniczenie pola widzenia
}

void keyPressed() {
  if (key == 'W' || key == 'w') {
    movingForward = true;
  } else if (key == 'S' || key == 's') {
    movingBackward = true;
  }
}

void keyReleased() {
  if (key == 'W' || key == 'w') {
    movingForward = false;
  } else if (key == 'S' || key == 's') {
    movingBackward = false;
  }
}

Preformatted text

To compute your shipDirection, you are using spherical coordinates which have a discontinuity at the poles. When you pass through the point looking straight up (or down), the camera flips around 180 degrees rather than turning upside down as you might expect.

How to fix it depends on what sort of camera behavior you think you want. What you have right now operates similar to Minecraft or Roblox or any first-person shooter (fps) where the camera up direction is always perpendicular to the ground plane – or in your case, the planetary orbital plane. In the case of a fps, they put a hard limit on the vertical view angle so that it stays bounded between 0 and PI (or yours might be between -PI/2 and PI/2) and usually with a little margin as well since you need your forward vector and your (0,1,0) up vector to stay different.

The alternative, if you want a full 3-D camera with arbitrary rotations, is rather more complex as you also need to track your up vector as you move around. This is often done by storing a 3x3 rotation matrix or a quaternion. For instance, in https://infinitefunspace.com/p5/fly/ where you can use the arrow keys for pitch and yaw and a and d to roll, I use a quaternion to track the view orientation. Toggle ‘/’ to reverse up the up and down if they feel backwards to you. ‘w’ and ‘s’ control speed. ‘[’ and ‘]’ change the number of objects. ‘l’ toggles snapping them to layers which lets you emulate flying over a ground plane. You can see the source code at https://infinitefunspace.com/p5/fly/p5Fly.js

For your case, though, where you have a well-oriented “ground plane” in the form of the planetary orbits, your spherical coordinate camera model is probably better. And certainly easier to code. So just put a limit on the up and down angle such that it doesn’t go past straight up or down and then it won’t flip around on you.

3 Likes

Thanks for your fast and helpfull reply. For now I put a limit on vertical view.