1
\$\begingroup\$

I am old. My high school maths is a long way in my past and I am struggling to devise an algorithm that will produce a 2D 'flying through space' effect like the old Windows screen saver. It has—

  • stars generated close to the screen centre, which move outwards at increasing speed, and
  • on a key press, a hyperspace effect is generated with the stars turning to lines stretching to the edge of the screen.

I've figured out that I can use arctan and x,y offsets from screen centre to identify a direction, but given that the trig functions work in a 90 degree field. My old brain is struggling to pull it all together.

How can I create this effect?

\$\endgroup\$
3
  • \$\begingroup\$ Can you clarify what specific trigonometry problem you need to solve? Is it just getting a 0-360 angle from signed x & y offsets (ie. the atan2 function)? \$\endgroup\$
    – DMGregory
    Commented Oct 14, 2018 at 16:13
  • \$\begingroup\$ There's this useful function called arctan2/atan2 that works in all 360 degrees. But actually, you probably don't need trigonometry here. \$\endgroup\$ Commented Oct 14, 2018 at 22:32
  • \$\begingroup\$ Related question on how to do a 3D "fantasy" version of this, with cloud textures \$\endgroup\$
    – Anko
    Commented Feb 18, 2019 at 3:50

2 Answers 2

1
\$\begingroup\$

Every frame—

  1. If enough time has elapsed since the last star, create a star at a slight random offset from the middle of the screen. You can get the x and y components of a random point on a circle by (as @Philipp mentions), using sin and cos on a random angle between 0 and 2π radians (= 360°).
  2. Multiply the x and y value of each star by 1+ε to taste of ε. I used a random value between 1.01 and 1.05. This makes stars move away from the middle, faster the further they are from the middle. Greater values of ε cause the star to move faster, creating the illusion that it's closer.
  3. Clear the screen. (Or don't, if you want the hyperspace effect. This makes the streaks of stars stay on screen instead of being cleared.)
  4. Draw a circle for each star at its position, with a radius scaled to its distance from the middle of the screen.
  5. Remove stars that have moved outside the window.

Here's a demo in a runnable JavaScript snippet, using HTML Canvas to render:

let canvas = document.getElementById('canvas');
let ctx = canvas.getContext('2d');

let middle = { x: canvas.width / 2, y: canvas.height / 2 }

// Each star is stored as an { x, y } object representing
// its offset from the middle of the screen

function canvasStarPosition(star) {
  return { x: middle.x + star.x,
           y: middle.y + star.y }
}

function starSize(star) {
  // Size is proportional to distance from the middle
  return Math.max(Math.abs(star.x), Math.abs(star.y)) / 100
}

function drawStar(ctx, star) {
  let { x, y } = canvasStarPosition(star)
  let r = starSize(star)
  ctx.fillStyle = 'white'
  ctx.beginPath()
  ctx.arc(x, y, r, 0, 2*Math.PI)
  ctx.fill()
}

let start = 0
let stars = []
let timeSinceLastStar = Infinity
let starInterval = 10000 // milliseconds

function makeStar() {
  let angle = Math.random() * 2*Math.PI
  stars.push({
    x : Math.cos(angle),
    y : Math.sin(angle),
    // The multiplier affects how quickly the star moves.
    // Varying this makes some stars appear closer (faster
    // movement), some further (slower movement).
    multiplier : 1.01 + Math.random() * 0.04
  })
}

function step(timestamp) {
  if (!start) start = timestamp
  var progress = timestamp - start
  
  // Create a new star if it's been long enough since
  // the previous one
  timeSinceLastStar += progress
  if (timeSinceLastStar > starInterval) {
    timeSinceLastStar = 0
    makeStar()
  }
  
  // Clear the screen.
  // If you want the hyperspace effect, skip this.
  ctx.clearRect(0,0,canvas.width, canvas.height)
  
  stars.forEach((s) => {
    // Increase the star's distance from the middle
    // proportionally to its current distance
    s.x *= s.multiplier
    s.y *= s.multiplier
    drawStar(ctx, s)
  })

  // Remove stars outside the view boundary
  let i = stars.length;
  while (--i) {
    let {x, y} = canvasStarPosition(stars[i])
    if (x < 0 || x > canvas.width || y < 0 || y > canvas.height) {
      stars.splice(i, 1)
    }
  }  
  window.requestAnimationFrame(step)
}
window.requestAnimationFrame(step)
<body style="background:black">
<canvas id="canvas" width="600" height="150"></canvas>
</body>

\$\endgroup\$
3
  • 2
    \$\begingroup\$ If ε is the same value, it would appear as if all the stars were on the same tube. You can use different values of ε to simulate stars with different distances. \$\endgroup\$ Commented Oct 16, 2018 at 0:26
  • \$\begingroup\$ @congusbongus Good point! I edited to add variation to that value in the demo, and explained what it does. \$\endgroup\$
    – Anko
    Commented Oct 16, 2018 at 18:08
  • \$\begingroup\$ You can also scale the star size by ε to make the sizes consistent with the distance. \$\endgroup\$ Commented Oct 16, 2018 at 21:56
0
\$\begingroup\$

Supposed you have a vector described by an angle and a length, you can convert it to its x:y form like this:

x = sin(angle) * length
y = cos(angle) * length
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .