2016-07-03 9 views
1

ich dieses snake game gebaut haben, wo die Schlangen sind animiert um einen Haufen von Würfel Maschen mit und THREE.BoxGeometry:Three.js: Animieren einer kastenartigen Geometrie

snake game

ich jede Schlange bestehen bevorzugen würde nur ein Netz und eine Geometrie, so dass ich leicht Texturen, abgerundete Kanten usw. hinzufügen kann.

Ich versuche, eine Reihe von 3D-Punkten zu nehmen und sie in eine einzige Geometrie zu konvertieren, die den kastenartigen Schlangen in der Demo ähnelt (wie mehrere THREE.BoxGeometry zusammengefügt).

Ich habe die THREE.CurvePath und THREE.ExtrudeGeometry mit zu erreichen versucht:

_makeSnakeGeometry(positions) { 
    const vectors = positions.map(p => new THREE.Vector3(...p)); 
    const curvePath = new THREE.CurvePath(); 
    const first = vectors[1]; 
    const last = vectors[vectors.length - 1]; 

    let curveStart = vectors[0]; 
    let previous = curveStart; 
    let previousDirection; 

    // Walk through the positions. If there is a change in direction, add 
    // a new curve to the curve path. 
    for (let vector of vectors.slice(1)) { 
    let direction = previous.clone().sub(vector); 

    if (vector.equals(first)) { 
     curveStart.sub(direction.clone().divideScalar(2)); 
    } 

    if (vector.equals(last)) { 
     previous.sub(previousDirection.clone().divideScalar(2)); 
    } 

    if ((previousDirection && !previousDirection.equals(direction)) || vector.equals(last)) { 
     curvePath.add(new THREE.LineCurve3(curveStart, previous)); 
     curveStart = previous; 
    } 

    previous = vector; 
    previousDirection = direction; 
    } 

    const p = Const.TILE_SIZE/2; 
    const points = [[-p, -p], [-p, p], [p, p], [p, -p]]; 
    const shape = new THREE.Shape(points.map(p => new THREE.Vector2(...p))); 
    const geometry = new THREE.ExtrudeGeometry(shape, { 
    steps: 20, 
    extrudePath: curvePath, 
    amount: 20 
    }); 

    return geometry; 
} 

Leider ist es ziemlich hässlich aussieht und wir am Ende mit abgerundeten Ecken, wo die Bahnrichtung ändert:

ugly snake game

Wenn ich die Option steps vergrößere, sieht das resultierende Netz weniger hässlich aus, aber die Geometrie weist aufgrund der glatten gekrümmten Kanten eine große Anzahl von Scheitelpunkten auf. Ich mache mir Sorgen um eine große Anzahl von Stützpunkten, da ich diese Geometrie möglicherweise in jedem Animationsrahmen neu erstellen muss.

Um die Geometrie zu animieren, habe ich versucht, geometry.morphTargets mit wenig Erfolg zu verwenden. Das Morph passiert, aber es sieht auch ziemlich hässlich aus. Vielleicht muss ich die Ecken der Geometrie manuell animieren?

Zusammenfassend meine Fragen sind:

  • Wie kann ich eine Geometrie mit minimalen Ecken erstellen, die zusammen mehrere Box-Geometrien pieced ähnelt?
  • Wie kann eine Geometrie animiert werden, wenn sich die zugrunde liegende Vertexanzahl ändern kann?
+0

Ich glaube, Sie brauchen die Triangulation für die Kette bei jeder Aktualisierung neu zu berechnen - es sei denn, Sie eine glatte Animation benötigen? –

+0

Idealerweise eine glatte Animation. Ich möchte das gleiche Ergebnis wie in der verknüpften Demo haben, aber mit einer einzigen Geometrie. –

+2

Dieses Problem kann durch Verwendung nicht optimierter Geometrie (d. H. Jede Box hat ihre eigene Geometrie) vereinfacht werden. –

Antwort

0

die Schlange Geometrie aufbauen, beenden I verschmelzenden Geometrien up:

_makeSnakeGeometry(positions) { 
    positions = positions.map(p => new THREE.Vector3(...p)); 
    const geometry = new THREE.Geometry(); 

    for (let i = 0; i < positions.length; i += 1) { 
    const position = positions[i]; 
    const mesh = makeVoxelMesh(Const.TILE_SIZE, { position }); 
    mesh.updateMatrix(); 
    geometry.merge(mesh.geometry, mesh.matrix); 
    } 

    return geometry; 
} 

makeVoxelMesh gibt ein einzelnen Würfel Geflecht für eine Position der Schlange. Ich schließe sie unter Verwendung geometry.merge zusammen und wiederhole den Prozess für jeden Animationsrahmen. Es ist nicht perfekt, da die Geometrie zusätzliche Scheitelpunkte und Flächen hat, die für eine so einfache Form nicht benötigt werden. Vielleicht gibt es einen Weg, sie nach der Tat zu reduzieren?

Um die Schlange zu animieren, finde ich die vier Ecken, die die Kopfseite umfassen. Ich mache es durch an den Scheitelpunkt-Positionen suchen und Flächennormalen:

_findHeadFaceVertices() { 
    const direction = this._direction.clone().multiplyScalar(1 + (Const.TILE_SIZE/10)); 
    const headFacePosition = new THREE.Vector3(...this.head).add(direction); 
    const { vertices, faces } = this.mesh.geometry; 

    // Sort vertices by distance to a point near the snake head and only keep 
    // the first few that are equidistant. 
    const closest = vertices 
    .map(v => [v.distanceTo(headFacePosition), v]) 
    .sort((a, b) => a[0] - b[0]) 
    .filter((pair, i, sorted) => pair[0] === sorted[0][0]) 
    .map(pair => pair[1]); 

    const result = []; 
    const seen = {}; 

    // Filter the remaining vertices by keeping only ones which belong to faces 
    // that point in the same direction as the snake. 
    for (let face of faces) { 
    for (let vertex of [vertices[face.a], vertices[face.b], vertices[face.c]]) { 
     const key = vertex.toArray().toString(); 
     if (!seen[key] && closest.includes(vertex) && face.normal.equals(this._direction)) { 
     seen[key] = true; 
     result.push(vertex); 
     } 
    } 
    } 

    return result; 
} 

Für Animation zu arbeiten, ich hatte this.mesh.geometry.verticesNeedUpdate = true; nach jedem Animationsrahmen zu setzen.

snake game