Przejdź do treści

Układ słoneczny za pomocą Gemini

  • przez

W niniejszym artykule poddam weryfikacji możliwości modelu Gemini w kontekście generowania symulacji Układu Słonecznego. Projekt zostanie zrealizowany w oparciu o z góry określony stos technologiczny obejmujący: React, Three.js oraz TypeScript.

Zaczynam od wysłania requestu do Gemini:

I want to create solar system with react and three.js I want to use typescript. Draw planets as cartoon and funny objects. I need step by step how to create application. Add zoom in and zoom out functionality. Add label on every planet.

Poniżej wklejam kolejne polecenia otrzymane z Gemini.

Step 1: Set up the Project

First, create a new React application with TypeScript and install the necessary 3D libraries. Open your terminal and run:

# 1. Create the app (using Vite is faster/modern, but Create React App works too)
npm create vite@latest solar-system -- --template react-ts
cd solar-system

# 2. Install Three.js and React Three Fiber ecosystem
npm install three @types/three @react-three/fiber @react-three/drei

Step 2: Clean up CSS

Make sure your canvas takes up the full screen. Open src/index.css (or App.css) and replace the content with this:

html, body, #root {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  background: #111; /* Dark space background */
  overflow: hidden;
}

Step 3: Create the Application

We will build this in a single file (App.tsx) for simplicity, but in a real app, you would split these into separate files.

Key concepts we are using:

  • MeshToonMaterial: This gives the planets a „cel-shaded” or cartoonish look rather than a realistic one.
  • @react-three/drei/Html: This lets us stick standard HTML text (labels) to 3D objects easily.
  • useFrame: This hook runs 60 times a second, allowing us to animate the planets orbiting.

Copy the following code into src/App.tsx:

import React, { useRef, useState } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls, Html, Stars } from '@react-three/drei';
import * as THREE from 'three';

// --- Types ---
interface PlanetProps {
  name: string;
  color: string;
  size: number;
  distance: number;
  speed: number;
  hasRing?: boolean;
}

// --- Planet Component ---
const Planet: React.FC<PlanetProps> = ({ name, color, size, distance, speed, hasRing }) => {
  // References to the mesh (for self-rotation) and the group (for orbit)
  const planetRef = useRef<THREE.Mesh>(null);
  const orbitRef = useRef<THREE.Group>(null);
  
  // State for hover effect
  const [hovered, setHover] = useState(false);

  // Animation Loop: Runs every frame
  useFrame((state, delta) => {
    // 1. Self-rotation (Spinning on axis)
    if (planetRef.current) {
      planetRef.current.rotation.y += delta * 0.5;
    }
    
    // 2. Orbit rotation (Revolving around the sun)
    if (orbitRef.current) {
      orbitRef.current.rotation.y += delta * speed;
    }
  });

  return (
    <group ref={orbitRef}>
      {/* We offset the planet by 'distance' on the X axis. 
        Because this group rotates at the center (0,0,0), the planet will orbit.
      */}
      <mesh 
        ref={planetRef} 
        position={[distance, 0, 0]}
        onPointerOver={() => setHover(true)}
        onPointerOut={() => setHover(false)}
        onClick={() => console.log(`You clicked ${name}!`)}
      >
        {/* 'toon' material gives it the cartoon look */}
        <sphereGeometry args={[size, 32, 32]} />
        <meshToonMaterial color={hovered ? 'white' : color} />
        
        {/* OPTIONAL: Ring for Saturn-like planets */}
        {hasRing && (
          <mesh rotation={[-Math.PI / 2, 0, 0]}>
            <torusGeometry args={[size * 1.5, 0.2, 16, 100]} />
            <meshToonMaterial color="orange" />
          </mesh>
        )}

        {/* Text Label */}
        <Html distanceFactor={15}>
          <div className="label-content">
            {name}
          </div>
        </Html>
      </mesh>
    </group>
  );
};

// --- Sun Component ---
const Sun = () => {
  return (
    <mesh>
      <sphereGeometry args={[2.5, 32, 32]} />
      {/* Emissive makes it glow, standard material is fine here */}
      <meshStandardMaterial emissive="#FFD700" emissiveIntensity={2} color="#FFD700" />
      <pointLight intensity={200} distance={100} decay={2} color="white" />
      <Html distanceFactor={20}>
        <div className="label-content" style={{ color: 'yellow', fontWeight: 'bold' }}>SUN</div>
      </Html>
    </mesh>
  );
};

// --- Main App Component ---
function App() {
  return (
    <>
      {/* Canvas is the entry point for Three.js. 
         'camera' sets the default zoom level. 
      */}
      <Canvas camera={{ position: [0, 20, 25], fov: 45 }}>
        
        {/* --- Lighting is crucial for Toon Material --- */}
        <ambientLight intensity={0.5} />
        <directionalLight position={[10, 10, 5]} intensity={1} />

        {/* --- Background Stars --- */}
        <Stars radius={100} depth={50} count={5000} factor={4} saturation={0} fade speed={1} />

        {/* --- Solar System Objects --- */}
        <Sun />
        
        {/* Props Explanation:
            distance: How far from center (sun)
            speed: Orbit speed (lower is slower)
            size: Radius of the planet 
        */}
        <Planet name="Mercury" color="#A5A5A5" size={0.4} distance={5} speed={0.8} />
        <Planet name="Venus"   color="#FFC107" size={0.7} distance={8} speed={0.6} />
        <Planet name="Earth"   color="#2196F3" size={0.8} distance={12} speed={0.4} />
        <Planet name="Mars"    color="#FF5722" size={0.6} distance={16} speed={0.3} />
        <Planet name="Jupiter" color="#FF9800" size={1.8} distance={22} speed={0.15} />
        <Planet name="Saturn"  color="#E6C17B" size={1.5} distance={30} speed={0.1} hasRing={true} />

        {/* --- Controls --- */}
        {/* OrbitControls gives you Zoom (scroll), Rotate (drag), and Pan (right-click drag) automatically */}
        <OrbitControls enablePan={true} enableZoom={true} enableRotate={true} />
        
      </Canvas>
    </>
  );
}

export default App;

Step 4: Add Simple Styles for the Labels

Because we used the <Html> component, we can style the labels using standard CSS. Add this to your src/index.css file to make the labels look „cartoonish” and readable:

.label-content {
  background: rgba(0, 0, 0, 0.7);
  color: white;
  padding: 4px 8px;
  border-radius: 4px;
  font-family: 'Comic Sans MS', 'Chalkboard SE', sans-serif; /* Cartoon font */
  font-size: 12px;
  white-space: nowrap;
  pointer-events: none; /* Let clicks pass through to the planet */
  border: 1px solid white;
}

Step 5: Run It

Back in your terminal, start the development server:

npm run dev

Wynik końcowy

Rzut oka na obrazek powyżej i werdykt jest jasny: sukces, ale nie w 100%. Mechanika działa, układ się kręci, ale… gdzieś zgubiło się kilka planet. Na szczęście to drobiazg. Mamy teraz dwie drogi: dopisać resztę ciał niebieskich samodzielnie albo wysłać Gemini z powrotem do pracy z prośbą o poprawkę.