Loops
Segunda parte · After Effects · Forma y personaje

Loops de
motion design

Cinco piezas en bucle sobre la forma: morphing, clones, profundidad, volumen y un personaje.
Loops
Recorrido

De la forma al personaje, en cinco piezas

Antes de animar

Cinco prácticas

Cierre

Loops
Antes de animar

Lo que viene de la primera parte

Esta parte da por sabido cómo se cierra un loop y cómo se trabaja. Si algo no suena, está explicado allá, con sus laboratorios.

Loops de motion design, primera parte
  • El empalme. El último frame tiene que enlazar con el primero; en un buen loop el paso no se nota.
  • Cinco formas de cerrar. loopOut, ciclos enteros, módulo, escala exponencial y circuito. Cada práctica de esta parte dice con cuál cierra.
  • El método. Planear con un style frame y un timing chart, bloquear, pulir y acabar.
  • El reparto. Un script dibuja la escena y la deja quieta; la animación se hace a mano, con keyframes, y con una expresión solo donde los keyframes no alcanzan.
Loops
Técnica

Siete herramientas nuevas

  • Set First Vertex

    Marca qué vértice va primero. Si no coincide entre dos formas, el morphing se tuerce.

    clic derecho en un vértice › Mask and Shape Path La gota
  • Un trazado pegado en Posición

    Se vuelve un recorrido, con un keyframe por vértice.

    Ctrl+C en Trazado › Ctrl+V en Posición El cardumen
  • Repeater

    Repite un grupo con un desplazamiento fijo. Las copias cambian juntas.

    Contenido › Agregar › Repeater El cardumen
  • Time Remapping

    Separa el tiempo de una capa del de la composición; con un módulo, repite un tramo.

    Ctrl+Alt+T El viaje
  • Track Matte

    Una capa usa la forma de la que tiene encima como ventana.

    columna Track Matte › Alpha Matte El trompo
  • Offset y Spherize

    Offset corre el contenido de una capa y lo hace dar la vuelta; Spherize lo curva como una esfera.

    Effect › Distort El trompo
  • Duik Ángela

    Huesos, controladores e IK para mover un personaje por piezas. Es gratuito.

    Ventana › Duik Ángela El paseo
Práctica 1
Práctica 1 · 6 s

La gota

Estirar y encogerAnticipaciónAcción complementariaTransformationValue change

Una gota cae, se aplasta en charco, del charco brota un tallo que abre un tulipán, y el tulipán se cierra en la gota del principio.

Es una sola forma de ocho vértices que pasa por seis siluetas.

◆ Apaga «Primer vértice» y mira cómo se tuerce
000 · 0.00 s

Radiografía

Práctica 1
Práctica 1 · Planeación

Timing chart

Una sola capa, «Forma», con keyframes de Trazado. Las cinco guías tienen los mismos ocho vértices, en el mismo orden, así que el morphing los lleva uno a uno.

El vértice 1, el más grande, está abajo al centro en las tres formas.

FrameTrazadoQué pasaPrincipio
0GotaCuelga en lo alto—
10Gota, copia del 0Empieza a caerEasy Ease Out
20CaídaSe estira por la velocidadSmear frame
21SalpicaduraSe aplasta contra el sueloEstirar y encoger
26CharcoSe asientaAcción compl.
34Charco, copia del 26Espera—
56BroteBrota el talloTransformation
72TulipánAbre en tres puntasTransformation
106Tulipán, copia del 72Florece—
114Brote, copia del 56Se cierraAnticipación
132Gota, copia del 0El tallo sube y se vuelve gotaEl empalme
Práctica 1
Práctica 1 · After Effects · 1 de 3

La caída

  1. Ejecutar el script. Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos File › Scripts › Run Script File, elegir 01-la-gota.jsx y contar en frames (Ctrl+clic en el tiempo actual). Se abre «La gota»: 6 s a 24 fps, la gota en lo alto y cinco guías tenues.
  2. El primer keyframe. En «Forma» › Contenido › Forma › Trazado 1, activar el cronómetro de Trazado en el frame 0. Copiar ese keyframe y pegarlo en el 10. Dos keyframes iguales: la gota cuelga quieta.
  3. Pegar una guía. En «Caída (guía)» › Contenido › Guía › Trazado 1, seleccionar Trazado y Ctrl+C. En «Forma», ir al 20, seleccionar su Trazado y Ctrl+V. La gota baja y se estira.
  4. El impacto. Lo mismo con «Salpicadura (guía)» en el 21 y con «Charco (guía)» en el 26. En el 20, alargada; en el 21, aplastada.
  5. Que caiga acelerando. El keyframe del 10, Ctrl+Mayús+F9 Easy Ease Out; el del 26, F9. Arranca despacio, llega rápido y el charco se asienta suave.
01-la-gota.jsx
// 01 · La gota · Loops de motion design, segunda parte
// Dibuja la gota en lo alto y la deja quieta, sobre un suelo y su sombra.
// La forma vive en una sola capa, «Forma», con el punto de anclaje en el suelo,
// justo debajo de ella. Al lado deja cinco capas guía con las otras formas por
// las que pasa: caída, salpicadura, charco, brote y tulipán. Las seis tienen
// los mismos ocho vértices, en el mismo orden, para que el morphing no se
// tuerza. Las guías no salen en el render.
// No crea keyframes ni expresiones.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
  var T = 6;                  // duración del loop, en segundos
  var SUELO = 820;            // la línea del suelo
  var FONDO = "#F4EEE6", PISO = "#E3D5C3", AZUL = "#3B82F6", GUIA = "#A72BC9", SOMBRA = "#2B2118";

  // Cada forma: ocho vértices [x, y, k] relativos al punto de apoyo. Con k = 1
  // el vértice es curvo; con k = 0, una esquina. Mismo orden en todas: abajo al
  // centro, abajo a la derecha, derecha, arriba a la derecha, arriba, y de
  // vuelta por la izquierda.
  var FORMAS = {
    "Gota":        [[0, -498, 1], [44, -516, 1], [62, -560, 1], [40, -614, 1], [0, -690, 0], [-40, -614, 1], [-62, -560, 1], [-44, -516, 1]],
    "Caída":       [[0, -40, 1], [24, -50, 1], [34, -110, 1], [22, -210, 1], [0, -340, 0], [-22, -210, 1], [-34, -110, 1], [-24, -50, 1]],
    "Salpicadura": [[0, 0, 1], [110, -2, 1], [180, -18, 1], [120, -40, 1], [0, -52, 1], [-120, -40, 1], [-180, -18, 1], [-110, -2, 1]],
    "Charco":      [[0, 0, 1], [80, -2, 1], [130, -12, 1], [90, -28, 1], [0, -34, 1], [-90, -28, 1], [-130, -12, 1], [-80, -2, 1]],
    "Brote":       [[0, 0, 1], [9, 0, 0], [9, -300, 0], [36, -352, 1], [0, -420, 0], [-36, -352, 1], [-9, -300, 0], [-9, 0, 0]],
    "Tulipán":     [[0, 0, 1], [9, 0, 0], [9, -300, 0], [84, -470, 0], [0, -428, 0], [-84, -470, 0], [-9, -300, 0], [-9, 0, 0]]
  };

  app.beginUndoGroup("La gota");

  var comp = app.project.items.addComp("La gota", 1080, 1080, 1, T, 24);
  comp.bgColor = rgb(FONDO);

  function rgb(h) {
    return [parseInt(h.substr(1, 2), 16) / 255, parseInt(h.substr(3, 2), 16) / 255, parseInt(h.substr(5, 2), 16) / 255];
  }
  function tr(capa) { return capa.property("ADBE Transform Group"); }
  function nuevoGrupo(capa, nombre) {
    var raiz = capa.property("ADBE Root Vectors Group");
    var i = raiz.addProperty("ADBE Vector Group").propertyIndex;
    raiz.property(i).name = nombre;
    return function () { return capa.property("ADBE Root Vectors Group").property(i).property("ADBE Vectors Group"); };
  }
  // Las tangentes salen de los vértices vecinos: curvas suaves sin dibujarlas a mano
  function trazado(puntos) {
    var n = puntos.length, v = [], ent = [], sal = [];
    for (var i = 0; i < n; i++) {
      var a = puntos[(i + n - 1) % n], b = puntos[(i + 1) % n], k = puntos[i][2] * 0.17;
      var t = [(b[0] - a[0]) * k, (b[1] - a[1]) * k];
      v.push([puntos[i][0], puntos[i][1]]); sal.push(t); ent.push([-t[0], -t[1]]);
    }
    var s = new Shape(); s.vertices = v; s.inTangents = ent; s.outTangents = sal; s.closed = true;
    return s;
  }
  function capaDeForma(nombre, grupo, puntos) {
    var capa = comp.layers.addShape();
    capa.name = nombre;
    var g = nuevoGrupo(capa, grupo);
    g().addProperty("ADBE Vector Shape - Group");
    g().property(1).property("ADBE Vector Shape").setValue(trazado(puntos));
    tr(capa).property("ADBE Position").setValue([540, SUELO]);
    return { capa: capa, g: g };
  }

  // ── Fondo, suelo y sombra ──
  comp.layers.addSolid(rgb(FONDO), "Fondo", 1080, 1080, 1, T);

  var suelo = comp.layers.addShape();
  suelo.name = "Suelo";
  var gs = nuevoGrupo(suelo, "Suelo");
  gs().addProperty("ADBE Vector Shape - Rect");
  gs().property(1).property("ADBE Vector Rect Size").setValue([1080, 1080 - SUELO]);
  gs().property(1).property("ADBE Vector Rect Position").setValue([540, (1080 + SUELO) / 2]);
  gs().addProperty("ADBE Vector Graphic - Fill");
  gs().property(2).property("ADBE Vector Fill Color").setValue(rgb(PISO));
  tr(suelo).property("ADBE Position").setValue([0, 0]);

  var sombra = comp.layers.addShape();
  sombra.name = "Sombra";
  var gb = nuevoGrupo(sombra, "Sombra");
  gb().addProperty("ADBE Vector Shape - Ellipse");
  gb().property(1).property("ADBE Vector Ellipse Size").setValue([120, 18]);
  gb().addProperty("ADBE Vector Graphic - Fill");
  gb().property(2).property("ADBE Vector Fill Color").setValue(rgb(SOMBRA));
  tr(sombra).property("ADBE Position").setValue([540, SUELO + 2]);
  tr(sombra).property("ADBE Opacity").setValue(18);

  // ── La forma que se anima, en lo alto ──
  var forma = capaDeForma("Forma", "Forma", FORMAS["Gota"]);
  forma.g().addProperty("ADBE Vector Graphic - Fill");
  forma.g().property(2).property("ADBE Vector Fill Color").setValue(rgb(AZUL));

  // ── Las guías: las otras cinco formas, en su sitio ──
  var GUIAS = ["Caída", "Salpicadura", "Charco", "Brote", "Tulipán"];
  for (var i = GUIAS.length - 1; i >= 0; i--) {
    var guia = capaDeForma(GUIAS[i] + " (guía)", "Guía", FORMAS[GUIAS[i]]);
    guia.g().addProperty("ADBE Vector Graphic - Stroke");
    guia.g().property(2).property("ADBE Vector Stroke Color").setValue(rgb(GUIA));
    guia.g().property(2).property("ADBE Vector Stroke Width").setValue(4);
    guia.capa.guideLayer = true;
    tr(guia.capa).property("ADBE Opacity").setValue(55);
  }

  comp.openInViewer();
  app.endUndoGroup();
})();

El frame 20 dura uno solo y casi no se ve quieto, pero en movimiento se lee como velocidad: es el smear frame. Para revisarlo frame a frame, Re Pág y Av Pág.

Práctica 1
Práctica 1 · After Effects · 2 de 3

El tulipán

  1. Brota. Copiar el keyframe del 26 y pegarlo en el 34; pegar «Brote (guía)» en el 56. F9 en los dos. Del charco sale un tallo con capullo.
  2. Abre. «Tulipán (guía)» en el 72, con F9. El capullo abre en tres puntas.
  3. Se cierra y vuelve a ser gota. Copiar el keyframe del 72 en el 106, el del 56 en el 114 y el del 0 en el 132. F9 en todos. El tulipán se cierra, el tallo sube y en el 132 es otra vez la gota.
  4. Revisar el primer vértice. Con Trazado seleccionado, el primer vértice se ve más grande en el visor. Si una forma se tuerce al pasar a la siguiente, clic derecho en el vértice que debe ir primero › Mask and Shape Path › Set First Vertex. Las formas cambian sin girar sobre sí mismas.
  5. Value change. En «Forma» › Contenido › Forma › Relleno 1 › Color, keyframes de azul #3B82F6 en el 0 y el 34, verde #2E9E6A en el 56, rosa #E2467F en el 72 y el 106, y azul otra vez en el 132. El del 34 y el del 106 repiten color y escribirlo no crea keyframe: se ponen con el botón central del navegador de keyframes, a la izquierda de Color Add or Remove Keyframe at Current Time. El color cambia con cada forma.
  6. Revisar el empalme. Comparar el 143 (Fin) con el 0 (Inicio). En los dos, la gota cuelga en lo alto.
Práctica 1
Práctica 1 · After Effects · 3 de 3

Pulir y personalizar

  1. Rebote al abrir. En «Forma», sin el vínculo de proporción, keyframes de Escala: 100, 100 en el 64; 92, 110 en el 70; 104, 97 en el 76; 100, 100 en el 82. El punto de anclaje ya está en el suelo. El tulipán se estira al abrir y se asienta.
  2. Anticipación al cerrar. Escala 100, 100 en el 100, con el navegador, y 108, 92 en el 106; vuelve a 100, 100 en el 114. Se agacha un instante antes de cerrarse.
  3. Se mece. Rotación: 0 en el 72, 4 en el 84, −3 en el 96 y 0 en el 106, con F9. El tulipán se mece sobre el tallo mientras florece.
  4. La sombra. En «Sombra», Escala 60 % en el 10, 120 % en el 21 y 100 % en el 26; 60 % otra vez en el 132. La sombra crece cuando la gota se acerca al suelo.
  5. Paleta propia. Los colores del relleno, del suelo y del fondo; las formas pueden cambiar mientras conserven sus ocho vértices. El mismo loop con otra historia: una hoja, una flama, una burbuja.
  6. Acabado. Desenfoque de movimiento y grano, como en la primera parte. La caída se barre un poco y la textura se mueve en cada frame.
  7. Exportar. Composición › Agregar a la cola de Adobe Media Encoder Add to Adobe Media Encoder Queue Ctrl+Alt+M: H.264 o GIF animado. 144 frames; el 143 enlaza con el 0.
Práctica 2
Práctica 2 · 12 s

El cardumen

ArcosAcción secundariaCloningOffset & Delay

Veinticuatro peces siguen a un guía por una ruta en ocho; cada uno mueve la cola y al fondo suben burbujas.

Solo el guía tiene keyframes: los demás leen su recorrido un poco después.

◆ Apaga «Offset & Delay» y mira cómo se amontonan
000 · 0.00 s

Radiografía

Peces

Práctica 2
Práctica 2 · Planeación

Lo que entrega el script

Dos peces quietos y la ruta que recorrerá el guía. El cardumen sale de duplicar «Pez 1».

Pez guíacoral; recorrerá la ruta
Pez 1ámbar; el que se clona
Colagrupo con el ancla donde se une al cuerpo
Ruta (guía)un ocho de 760 × 440, con ocho vértices
Burbujasuna sola; el Repeater pondrá las demás
Algas, Rayos y Luzel ambiente, quieto
Fondoazul de mar

Cómo cierra

Cada ciclo divide los 12 s del loop. Los clones leen la ruta del guía con un módulo de 6 s, así que su retraso tampoco abre un hueco.

PiezaCicloVeces en 12 s
Ruta del guía144 frames · 6 s2
Coleteo12 frames24
Burbujas24 frames · 1 s12
Cada clonla ruta, 0.15 s después que el anterior2
Práctica 2
Práctica 2 · After Effects · 1 de 3

El guía

  1. Ejecutar el script 02-el-cardumen.jsx y contar en frames. Se abre «El cardumen»: 12 s, dos peces y una ruta en ocho, tenue.
  2. Copiar la ruta. En «Ruta (guía)» › Contenido › Ruta › Trazado 1, seleccionar Trazado y Ctrl+C. Seleccionar «Pez guía», pulsar P, clic en el nombre Posición y Ctrl+V. Aparecen keyframes de Posición y el pez salta a la ruta.
  3. Seis segundos por vuelta. Con todos los keyframes seleccionados (clic en el nombre Posición), Alt+arrastrar el último hasta el 144: los demás se reparten en proporción. Los keyframes ocupan del 0 al 144.
  4. Velocidad pareja. Seleccionar los keyframes de en medio, todos menos el primero y el último; clic derecho › Rove Across Time. El pez avanza igual de rápido en todo el ocho.
  5. De frente y sin fin. Capa › Transformar › Auto-Orient › Orient Along Path Ctrl+Alt+O; luego Alt+clic en el cronómetro de Posición y loopOut("cycle"). Dos vueltas en 12 s, siempre mirando hacia donde nada.
02-el-cardumen.jsx
// 02 · El cardumen · Loops de motion design, segunda parte
// Dibuja el fondo del mar y dos peces quietos: «Pez guía», que recorrerá la ruta,
// y «Pez 1», el que se clona. Cada pez tiene la cola en su propio grupo, con el
// punto de anclaje donde se une al cuerpo, y mira hacia la derecha.
// También deja la ruta en forma de ocho como capa guía (no sale en el render),
// lista para copiar su trazado en la Posición del guía, y una burbuja.
// No crea keyframes ni expresiones.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
  var T = 12;                       // duración del loop, en segundos
  var CENTRO = [540, 500], A = 380, B = 220;   // la ruta: x = A cos t, y = B sen 2t
  var MAR = "#0B3D6B", LUZ = "#2E86C1", GUIA = "#FFD166";
  var CORAL = "#FF6F59", AMBAR = "#FFB347", ALGA = "#2A9D8F", TINTA = "#14213D";

  app.beginUndoGroup("El cardumen");

  var comp = app.project.items.addComp("El cardumen", 1080, 1080, 1, T, 24);
  comp.bgColor = rgb(MAR);

  function rgb(h) {
    return [parseInt(h.substr(1, 2), 16) / 255, parseInt(h.substr(3, 2), 16) / 255, parseInt(h.substr(5, 2), 16) / 255];
  }
  function tr(capa) { return capa.property("ADBE Transform Group"); }
  function nuevoGrupo(capa, nombre) {
    var raiz = capa.property("ADBE Root Vectors Group");
    var i = raiz.addProperty("ADBE Vector Group").propertyIndex;
    raiz.property(i).name = nombre;
    return function () { return capa.property("ADBE Root Vectors Group").property(i).property("ADBE Vectors Group"); };
  }
  function transformacion(g) { return g().parentProperty.property("ADBE Vector Transform Group"); }
  function forma(v, ent, sal, cerrada) {
    var s = new Shape(); s.vertices = v; s.inTangents = ent; s.outTangents = sal; s.closed = cerrada; return s;
  }
  function sinTangentes(n) { var t = []; for (var i = 0; i < n; i++) t.push([0, 0]); return t; }
  function relleno(g, color, opacidad) {
    g().addProperty("ADBE Vector Graphic - Fill");
    var f = g().property(g().numProperties);
    f.property("ADBE Vector Fill Color").setValue(rgb(color));
    if (opacidad != null) f.property("ADBE Vector Fill Opacity").setValue(opacidad);
  }
  function trazo(g, color, grosor, opacidad) {
    g().addProperty("ADBE Vector Graphic - Stroke");
    var t = g().property(g().numProperties);
    t.property("ADBE Vector Stroke Color").setValue(rgb(color));
    t.property("ADBE Vector Stroke Width").setValue(grosor);
    t.property("ADBE Vector Stroke Line Cap").setValue(2);
    if (opacidad != null) t.property("ADBE Vector Stroke Opacity").setValue(opacidad);
  }
  function elipse(capa, nombre, centro, tam, color, opacidad) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Ellipse");
    g().property(1).property("ADBE Vector Ellipse Size").setValue(tam);
    g().property(1).property("ADBE Vector Ellipse Position").setValue(centro);
    relleno(g, color, opacidad);
    return g;
  }
  function poligono(capa, nombre, puntos, color, opacidad) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Group");
    g().property(1).property("ADBE Vector Shape").setValue(forma(puntos, sinTangentes(puntos.length), sinTangentes(puntos.length), true));
    relleno(g, color, opacidad);
    return g;
  }
  // una capa de forma que dibuja en coordenadas de la composición
  function pieza(nombre) {
    var capa = comp.layers.addShape();
    capa.name = nombre;
    tr(capa).property("ADBE Position").setValue([0, 0]);
    return capa;
  }

  // ── El mar ──
  comp.layers.addSolid(rgb(MAR), "Fondo", 1080, 1080, 1, T);
  var luz = pieza("Luz");
  elipse(luz, "Luz", [540, -100], [1400, 760], LUZ, 35);
  var rayos = pieza("Rayos");
  poligono(rayos, "Rayo 1", [[180, 0], [300, 0], [520, 1080], [380, 1080]], "#FFFFFF", 6);
  poligono(rayos, "Rayo 2", [[520, 0], [600, 0], [760, 1080], [650, 1080]], "#FFFFFF", 5);
  poligono(rayos, "Rayo 3", [[820, 0], [900, 0], [1040, 1080], [960, 1080]], "#FFFFFF", 4);

  var algas = pieza("Algas");
  var TALLOS = [[[110, 1090], [128, 960], [104, 860]], [[180, 1090], [196, 990], [176, 900]], [[930, 1090], [948, 950], [926, 840]]];
  for (var a = 0; a < TALLOS.length; a++) {
    var ga = nuevoGrupo(algas, "Alga " + (a + 1));
    ga().addProperty("ADBE Vector Shape - Group");
    ga().property(1).property("ADBE Vector Shape").setValue(forma(TALLOS[a], [[0, 0], [0, 40], [0, 30]], [[0, -40], [0, -30], [0, 0]], false));
    trazo(ga, ALGA, 16);
  }

  // ── Una burbuja: el Repeater hará las demás ──
  var burbujas = comp.layers.addShape();
  burbujas.name = "Burbujas";
  var gb = nuevoGrupo(burbujas, "Burbuja");
  gb().addProperty("ADBE Vector Shape - Ellipse");
  gb().property(1).property("ADBE Vector Ellipse Size").setValue([22, 22]);
  trazo(gb, "#FFFFFF", 3, 70);
  tr(burbujas).property("ADBE Position").setValue([300, 1010]);

  // ── La ruta en ocho, como guía: x = A cos t, y = B sen 2t, en ocho vértices ──
  var ruta = pieza("Ruta (guía)");
  var v = [], ent = [], sal = [], paso = Math.PI / 4, h = paso / 3;
  for (var k = 0; k < 8; k++) {
    var t = k * paso;
    v.push([CENTRO[0] + A * Math.cos(t), CENTRO[1] + B * Math.sin(2 * t)]);
    var d = [-A * Math.sin(t) * h, 2 * B * Math.cos(2 * t) * h];
    sal.push(d); ent.push([-d[0], -d[1]]);
  }
  var gr = nuevoGrupo(ruta, "Ruta");
  gr().addProperty("ADBE Vector Shape - Group");
  gr().property(1).property("ADBE Vector Shape").setValue(forma(v, ent, sal, true));
  trazo(gr, GUIA, 3, 80);
  ruta.guideLayer = true;
  tr(ruta).property("ADBE Opacity").setValue(60);

  // ── Los peces: el cuerpo mira a la derecha; la cola gira desde donde se une ──
  function pez(nombre, color, pos) {
    var capa = comp.layers.addShape();
    capa.name = nombre;
    elipse(capa, "Pupila", [35, -8], [6, 6], TINTA);
    elipse(capa, "Ojo", [33, -8], [14, 14], "#FFFFFF");
    poligono(capa, "Aleta", [[-4, -24], [-34, -44], [-26, -20]], color);
    elipse(capa, "Cuerpo", [0, 0], [124, 58], color);
    var cola = poligono(capa, "Cola", [[-48, 0], [-96, -32], [-88, 0], [-96, 32]], color);
    transformacion(cola).property("ADBE Vector Anchor").setValue([-48, 0]);
    transformacion(cola).property("ADBE Vector Position").setValue([-48, 0]);
    tr(capa).property("ADBE Position").setValue(pos);
    return capa;
  }
  pez("Pez 1", AMBAR, [700, 360]);
  pez("Pez guía", CORAL, v[0]);

  comp.openInViewer();
  app.endUndoGroup();
})();

Pegar un trazado en Posición crea un keyframe por vértice. La ruta está cerrada, así que el último keyframe vuelve al primer punto y loopOut repite la vuelta sin salto.

Práctica 2
Práctica 2 · After Effects · 2 de 3

Los clones

  1. La cola. En «Pez 1» › Contenido › Cola › Transformar: Cola › Rotación: 18 en el 0, −18 en el 6 y 18 en el 12; F9 y loopOut("cycle"). Lo mismo en la cola del guía. Las dos colas aletean dos veces por segundo.
  2. Seguir al guía. En «Pez 1», Alt+clic en el cronómetro de Posición y escribir la primera expresión. El pez nada detrás del guía, un poco a un lado.
  3. Mirar hacia donde nada. Alt+clic en Rotación y la segunda expresión. El pez gira con la curva.
  4. Veinticuatro. Seleccionar «Pez 1» y pulsar Ctrl+D veintidós veces. Aparecen de «Pez 2» a «Pez 23»; cada uno lee su número y el cardumen se estira en una estela.
  5. Revisar el empalme. Comparar el 287 con el 0. Los peces están en el mismo punto de la ruta.
Expresiones · Posición y Rotación de «Pez 1»
// Posición: sigue al guía n × 0.15 s después, un poco a un lado
var n = parseInt(thisLayer.name.split(" ")[1]);   // «Pez 7» → 7
var g = thisComp.layer("Pez guía").transform.position;
var t = (time - n * 0.15 + 6) % 6;                // la ruta dura 6 s
var v = g.velocityAtTime(t), l = length(v);
seedRandom(n, true);
add(g.valueAtTime(t), mul([-v[1] / l, v[0] / l], random(-70, 70)))

// Rotación: hacia donde se mueve el guía en ese instante
var n = parseInt(thisLayer.name.split(" ")[1]);
var v = thisComp.layer("Pez guía").transform.position.velocityAtTime((time - n * 0.15 + 6) % 6);
radiansToDegrees(Math.atan2(v[1], v[0]))

Con 24 peces, poner keyframes a mano sería repetir la ruta 23 veces. El módulo hace que los primeros peces, al empezar el loop, lean el final de la vuelta anterior.

Práctica 2
Práctica 2 · After Effects · 3 de 3

Pulir y personalizar

  1. Burbujas con Repeater. En «Burbujas» › Contenido, Agregar › Repeater: 6 copias; en Transformar: Repetidor 1, Posición 70, −90 y Opacidad final 0 %. Seis burbujas en diagonal, cada vez más tenues.
  2. Que suban. Posición de «Burbujas»: 300, 1010 en el 0 y 370, 920 en el 24, lineales, con loopOut("cycle"). Cada segundo una burbuja ocupa el lugar de la siguiente: suben sin salto.
  3. Colas a destiempo. En la Rotación de la cola de «Pez 1», cambiar loopOut por valueAtTime((time + parseInt(thisLayer.name.split(" ")[1]) * 0.07) % 0.5) y copiar la expresión de «Pez 2» a «Pez 23». El guía conserva loopOut: su nombre no lleva número. Cada pez aletea a su ritmo.
  4. Tamaños distintos. En la Escala de «Pez 1», seedRandom(parseInt(thisLayer.name.split(" ")[1]), true); var s = random(70, 105); [s, s], y copiarla de «Pez 2» a «Pez 23». Peces chicos y grandes: el cardumen gana profundidad.
  5. Acabado. Desenfoque de movimiento en los peces y grano. Las colas se barren al aletear.
  6. Exportar. Ctrl+Alt+M, H.264 o GIF animado. 288 frames; el 287 enlaza con el 0.
Práctica 3
Práctica 3 · 12 s

El viaje

Puesta en escenaAcción secundariaParallaxOffset & Delay

Un tren cruza un paisaje de seis planos al atardecer: cuanto más cerca está un plano, más rápido corre.

El tren no avanza; el que se mueve es el paisaje.

◆ Apaga «Parallax» y mira cómo se aplana
000 · 0.00 s

Radiografía

Práctica 3
Práctica 3 · Planeación

Seis planos, cinco velocidades

El script dibuja cada plano como un patrón repetido varias veces seguidas. Si un plano avanza exactamente un patrón, el dibujo vuelve a ser idéntico y el salto no se ve; loopOut lo repite sin fin.

El cielo y el sol no se mueven. La velocidad crece con la cercanía, y eso es lo que la vista lee como profundidad.

PlanoPatrónVelocidadUn patrón enPosición
Montañas540 px90 px/s144 frames0, 0 → −540, 0
Colinas540 px180 px/s72 frames0, 0 → −540, 0
Árboles360 px360 px/s24 frames0, 0 → −360, 0
Durmientes120 px720 px/s4 frames0, 0 → −120, 0
Postes720 px1440 px/s12 frames0, 0 → −720, 0
Cielo y sol—quietos——

Cada «un patrón en» divide los 288 frames del loop: el empalme cierra en todos los planos.

Práctica 3
Práctica 3 · After Effects · 1 de 3

Los planos

  1. Ejecutar el script 03-el-viaje.jsx y contar en frames. Se abre «El viaje»: 12 s, el tren detenido frente al paisaje.
  2. El plano del fondo. Seleccionar «Montañas» y pulsar P: su Posición es 0, 0. Activar el cronómetro en el 0 y, en el 144, escribir −540, 0. Las montañas se corren hacia la izquierda.
  3. Sin fin. Los dos keyframes ya son lineales. Alt+clic en el cronómetro y loopOut("cycle"). En el 144 el dibujo es idéntico al del 0: el salto no se nota.
  4. Los demás planos. Lo mismo en «Colinas», «Árboles», «Durmientes» y «Postes», con los valores de la tabla. Cuanto más cerca, más rápido corre cada plano.
  5. Revisar. Previsualizar con la barra espaciadora. El paisaje gana fondo y el tren parece avanzar.
03-el-viaje.jsx
// 03 · El viaje · Loops de motion design, segunda parte
// Dibuja un tren detenido frente a un paisaje de planos, al atardecer. Cada plano
// que se moverá es un patrón que se repite cada W píxeles, dibujado las veces
// necesarias para cubrir la pantalla y un patrón más: montañas y colinas cada
// 540 px, árboles cada 360, durmientes cada 120 y postes cada 720. Todos están
// en coordenadas de la composición, con la Posición en 0, 0.
// Las seis ruedas tienen el punto de anclaje en su centro. El humo es una
// precomposición, «Bocanada», que dura lo mismo que el loop: la bocanada se anima en
// su primer segundo y Time Remapping la repite. Está puesta cuatro veces.
// No crea keyframes ni expresiones.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
  var T = 12;               // duración del loop, en segundos
  var CIELO = "#FFD6A5", SOL = "#FF9F68", MONTE = "#C8A2C8", COLINA = "#9A8BC6", ARBOL = "#4E6E9A";
  var TIERRA = "#3D405B", RIEL = "#8D99AE", DURMIENTE = "#2B2D42", POSTE = "#22223B";
  var ROJO = "#E63946", ROJO_2 = "#C1121F", AZUL = "#457B9D", MARINO = "#1D3557", HUMO = "#F1FAEE";

  app.beginUndoGroup("El viaje");

  function rgb(h) {
    return [parseInt(h.substr(1, 2), 16) / 255, parseInt(h.substr(3, 2), 16) / 255, parseInt(h.substr(5, 2), 16) / 255];
  }
  function tr(capa) { return capa.property("ADBE Transform Group"); }
  function nuevoGrupo(capa, nombre) {
    var raiz = capa.property("ADBE Root Vectors Group");
    var i = raiz.addProperty("ADBE Vector Group").propertyIndex;
    raiz.property(i).name = nombre;
    return function () { return capa.property("ADBE Root Vectors Group").property(i).property("ADBE Vectors Group"); };
  }
  function relleno(g, color, opacidad) {
    g().addProperty("ADBE Vector Graphic - Fill");
    var f = g().property(g().numProperties);
    f.property("ADBE Vector Fill Color").setValue(rgb(color));
    if (opacidad != null) f.property("ADBE Vector Fill Opacity").setValue(opacidad);
  }
  function trazo(g, color, grosor) {
    g().addProperty("ADBE Vector Graphic - Stroke");
    var t = g().property(g().numProperties);
    t.property("ADBE Vector Stroke Color").setValue(rgb(color));
    t.property("ADBE Vector Stroke Width").setValue(grosor);
    t.property("ADBE Vector Stroke Line Cap").setValue(2);
  }
  function rect(capa, nombre, x0, y0, x1, y1, color, redondeo, opacidad) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Rect");
    g().property(1).property("ADBE Vector Rect Size").setValue([x1 - x0, y1 - y0]);
    g().property(1).property("ADBE Vector Rect Position").setValue([(x0 + x1) / 2, (y0 + y1) / 2]);
    if (redondeo) g().property(1).property("ADBE Vector Rect Roundness").setValue(redondeo);
    relleno(g, color, opacidad);
    return g;
  }
  function elipse(capa, nombre, cx, cy, w, h, color, opacidad) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Ellipse");
    g().property(1).property("ADBE Vector Ellipse Size").setValue([w, h]);
    g().property(1).property("ADBE Vector Ellipse Position").setValue([cx, cy]);
    relleno(g, color, opacidad);
    return g;
  }
  function poligono(capa, nombre, puntos, color) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Group");
    var s = new Shape(); s.vertices = puntos; s.closed = true;
    g().property(1).property("ADBE Vector Shape").setValue(s);
    relleno(g, color);
    return g;
  }
  function linea(capa, nombre, a, b, color, grosor) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Group");
    var s = new Shape(); s.vertices = [a, b]; s.closed = false;
    g().property(1).property("ADBE Vector Shape").setValue(s);
    trazo(g, color, grosor);
    return g;
  }
  function pieza(comp, nombre) {
    var capa = comp.layers.addShape();
    capa.name = nombre;
    tr(capa).property("ADBE Position").setValue([0, 0]);
    return capa;
  }
  // cuántas copias de un patrón de ancho W cubren la pantalla y un patrón más
  function copias(W) { return Math.ceil((1080 + W) / W); }

  // ── La bocanada: se anima en su primer segundo y Time Remapping la repite ──
  var bocanada = app.project.items.addComp("Bocanada", 1080, 1080, 1, T, 24);
  var humo = bocanada.layers.addShape();
  humo.name = "Humo";
  elipse(humo, "Humo", 0, 0, 60, 60, HUMO, 85);
  tr(humo).property("ADBE Position").setValue([785, 572]);

  var comp = app.project.items.addComp("El viaje", 1080, 1080, 1, T, 24);
  comp.bgColor = rgb(CIELO);

  // ── Cielo y sol ──
  comp.layers.addSolid(rgb(CIELO), "Cielo", 1080, 1080, 1, T);
  var sol = pieza(comp, "Sol");
  elipse(sol, "Halo", 790, 330, 290, 290, SOL, 30);
  elipse(sol, "Sol", 790, 330, 180, 180, SOL);

  // ── Los planos, del más lejano al más cercano ──
  var montes = pieza(comp, "Montañas");
  for (var i = 0; i < copias(540); i++) {
    var x = i * 540;
    poligono(montes, "Montañas " + (i + 1), [[x, 650], [x + 120, 470], [x + 230, 560], [x + 360, 420], [x + 540, 650]], MONTE);
  }
  var colinas = pieza(comp, "Colinas");
  rect(colinas, "Base", 0, 700, 540 * copias(540), 820, COLINA);
  for (i = 0; i < copias(540); i++) {
    x = i * 540;
    elipse(colinas, "Colina " + (2 * i + 1), x + 130, 705, 420, 220, COLINA);
    elipse(colinas, "Colina " + (2 * i + 2), x + 400, 725, 380, 190, COLINA);
  }
  var arboles = pieza(comp, "Árboles");
  for (i = 0; i < copias(360); i++) {
    x = i * 360;
    rect(arboles, "Tronco " + (2 * i + 1), x + 82, 690, x + 98, 790, ARBOL);
    elipse(arboles, "Copa " + (2 * i + 1), x + 90, 662, 92, 92, ARBOL);
    rect(arboles, "Tronco " + (2 * i + 2), x + 254, 710, x + 268, 790, ARBOL);
    elipse(arboles, "Copa " + (2 * i + 2), x + 261, 690, 70, 70, ARBOL);
  }

  // ── El terraplén y la vía ──
  var tierra = pieza(comp, "Terraplén");
  rect(tierra, "Terraplén", 0, 790, 1080, 1080, TIERRA);
  var riel = pieza(comp, "Riel");
  linea(riel, "Riel", [0, 800], [1080, 800], RIEL, 6);
  var durmientes = pieza(comp, "Durmientes");
  for (i = 0; i < copias(120); i++) rect(durmientes, "Durmiente " + (i + 1), i * 120 + 20, 806, i * 120 + 64, 822, DURMIENTE);

  // ── El tren ──
  var vagon2 = pieza(comp, "Vagón 2");
  rect(vagon2, "Ventanas", 166, 680, 324, 712, CIELO, 4);
  rect(vagon2, "Caja", 140, 660, 350, 770, MARINO, 10);
  var vagon1 = pieza(comp, "Vagón 1");
  linea(vagon1, "Enganche", [350, 750], [370, 750], DURMIENTE, 6);
  rect(vagon1, "Ventanas", 396, 680, 554, 712, CIELO, 4);
  rect(vagon1, "Caja", 370, 660, 580, 770, AZUL, 10);
  var loco = pieza(comp, "Locomotora");
  linea(loco, "Enganche", [580, 750], [600, 750], DURMIENTE, 6);
  poligono(loco, "Quitapiedras", [[820, 730], [858, 778], [820, 778]], DURMIENTE);
  rect(loco, "Ventana", 615, 595, 675, 630, CIELO, 4);
  rect(loco, "Chimenea", 770, 590, 800, 642, DURMIENTE, 3);
  rect(loco, "Cabina", 600, 580, 690, 642, ROJO_2, 6);
  rect(loco, "Caldera", 600, 640, 820, 770, ROJO, 10);

  var CENTROS = [640, 780, 410, 540, 180, 310];
  for (i = 0; i < CENTROS.length; i++) {
    var rueda = comp.layers.addShape();
    rueda.name = "Rueda " + (i + 1);
    elipse(rueda, "Eje", 0, 0, 12, 12, HUMO);
    linea(rueda, "Rayo 1", [-24, 0], [24, 0], HUMO, 4);
    linea(rueda, "Rayo 2", [0, -24], [0, 24], HUMO, 4);
    elipse(rueda, "Rueda", 0, 0, 60, 60, MARINO);
    tr(rueda).property("ADBE Position").setValue([CENTROS[i], 768]);
  }

  // ── Cuatro bocanadas, todas en el mismo punto hasta que se desfasen ──
  for (i = 0; i < 4; i++) {
    var capa = comp.layers.add(bocanada);
    capa.name = "Bocanada " + (i + 1);
  }

  // ── Los postes, en primer plano ──
  var postes = pieza(comp, "Postes");
  for (i = 0; i < copias(720); i++) {
    x = i * 720;
    rect(postes, "Travesaño " + (i + 1), x + 262, 404, x + 358, 418, POSTE, 2);
    rect(postes, "Poste " + (i + 1), x + 301, 380, x + 319, 1080, POSTE);
  }

  comp.openInViewer();
  app.endUndoGroup();
})();

Aquí loopOut hace el trabajo del módulo: repite el tramo de un patrón y, como el dibujo se repite igual, el corte cae justo donde un patrón termina y empieza el siguiente.

Práctica 3
Práctica 3 · After Effects · 2 de 3

El tren

  1. Las ruedas. Seleccionar las seis «Rueda» y pulsar R. En el 0, Alt+Mayús+R pone un keyframe en las seis; en el 6, escribir 360. Con loopOut("cycle") en «Rueda 1», copiar la expresión a las otras cinco. Una vuelta cada 6 frames.
  2. El traqueteo. Posición de «Locomotora» y de «Vagón 2»: 0, 0 en el 0; 0, 3 en el 3; 0, 0 en el 6, con loopOut("cycle"). «Vagón 1», al revés: 0, 3; 0, 0; 0, 3. Los vagones saltan a destiempo, uno sí y uno no.
  3. Una bocanada. Doble clic en «Bocanada 1» para entrar a la precomposición. En «Humo», del 0 al 23: Escala de 30 % a 170 %, Opacidad de 90 a 0 y Posición de 785, 572 a 700, 430. El humo crece, sube hacia atrás y se desvanece en un segundo.
  4. Cuatro bocanadas. De vuelta en «El viaje», seleccionar las cuatro «Bocanada», Capa › Tiempo › Enable Time Remapping Ctrl+Alt+T. En «Bocanada 1», Alt+clic en su cronómetro y la expresión de la derecha; copiarla a las otras tres. Cada bocanada nace un cuarto de segundo después de la anterior.
Expresión · Time Remap de «Bocanada 1»
// Repite el primer segundo de la precomposición;
// cada capa, 0.25 s después de la anterior
var n = parseInt(thisLayer.name.split(" ")[1]);
(time + n * 0.25) % 1

Time Remapping dice qué segundo de la precomposición se ve en cada momento. Con el módulo, siempre es uno entre 0 y 1: la bocanada se repite sin keyframes nuevos.

Práctica 3
Práctica 3 · After Effects · 3 de 3

Pulir y personalizar

  1. Humo más denso. Duplicar las cuatro bocanadas con Ctrl+D y revisar que las copias se llamen de «Bocanada 5» a «Bocanada 8»; en las ocho, cambiar 0.25 por 0.125. Ocho bocanadas, una cada tres frames.
  2. Aire entre los planos. Opacidad de «Montañas» al 70 % y de «Colinas» al 85 %. Lo lejano se pierde en el color del cielo, como en un atardecer real.
  3. Barrido. Desenfoque de movimiento en «Postes» y «Durmientes», y el botón de la línea de tiempo. Lo más cercano se barre; lo lejano sigue nítido.
  4. Otro paisaje. En «Montañas», borrar los grupos 2 y 3 y redibujar el trazado del 1 entre x = 0 y x = 540, con los dos extremos a la misma altura. Agregar › Repeater: 3 copias y, en Transformar: Repetidor 1, Posición 540, 0. Un desierto, una ciudad o el mar, a la misma velocidad: el patrón se repite solo.
  5. Acabado. Grano, como en la primera parte. Una textura que cambia en cada frame.
  6. Exportar. Ctrl+Alt+M, H.264 o GIF animado. 288 frames; el 287 enlaza con el 0.
Práctica 4
Práctica 4 · 8 s

El trompo

Dibujo sólidoArcosMaskingDimensionality

Un trompo de colores gira sobre la punta y se tambalea. Es plano: el volumen sale de cómo corren las franjas, de una luz que no gira y de su sombra.

Ni una capa 3D.

◆ Apaga «Curvatura» y mira cómo se aplana
000 · 0.00 s

Radiografía

Práctica 4
Práctica 4 · Planeación

Lo que entrega el script

El trompo quieto, por capas, en el orden en que tienen que quedar para el mate.

Cabezala perilla de arriba
Brilloblanco al 40 %; no gira
Sombra propiamedia luna al 28 %; no gira
Siluetael cuerpo, que servirá de mate
Franjasprecomposición de 320 × 400; el patrón se repite cada 320 px
Puntade metal, apoyada en 540, 842
Sombra, Piso y Fondola sombra, una elipse sobre el piso

Tres pistas de volumen

PistaQué hace el ojoCon qué
Las franjas corren de ladoLee un giroOffset
Se aprietan en los bordesLee un cuerpo redondoSpherize
La luz se queda quietaLee que gira el cuerpo, no el dibujoBrillo y sombra propia

Las franjas recorren su patrón de 320 px cada 8 frames: 24 vueltas en los 192 del loop. El tambaleo dura 48 frames y cabe cuatro veces.

Práctica 4
Práctica 4 · After Effects · 1 de 3

El giro

  1. Ejecutar el script 04-el-trompo.jsx y contar en frames. Se abre «El trompo»: 8 s; las franjas asoman como un rectángulo detrás del cuerpo blanco.
  2. El mate. En la capa «Franjas», columna Track Matte: elegir «Silueta», en Alpha Matte. La silueta tiene que estar justo encima. Las franjas solo se ven dentro del cuerpo y la silueta se apaga.
  3. Que corran. En «Franjas», Effect › Distort › Offset. Cronómetro de Shift Center To en el 0 con 160, 200; en el 8, 480, 200. Las franjas corren de lado, planas.
  4. Sin fin. Los dos keyframes ya son lineales: Alt+clic y loopOut("cycle"). 24 vueltas en 8 s, sin salto.
  5. Curvatura. Effect › Distort › Spherize en «Franjas», debajo de Offset: Radius 175 y Center of Sphere 160, 200. Las franjas se aprietan en los bordes y el trompo se ve redondo.
04-el-trompo.jsx
// 04 · El trompo · Loops de motion design, segunda parte
// Dibuja un trompo de colores parado sobre la punta, todavía sin girar.
// Las franjas viven en su propia precomposición, «Franjas», de 320 × 400: ocho
// franjas verticales de 40 px, así que el patrón se repite cada 320 px y el
// efecto Offset puede correrlo sin que se note la unión. Justo encima está la
// «Silueta», que servirá de mate. La luz y la sombra propia ya están dibujadas
// encima, y la sombra en el piso, debajo.
// No crea keyframes, expresiones, mates ni efectos.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
  var T = 8;                  // duración del loop, en segundos
  var PUNTA = [540, 842];     // donde el trompo toca el piso
  var FONDO = "#FFF1DC", PISO = "#F3D9B1", MADERA = "#B07A4A", VETA = "#8B5A2B", METAL = "#8A8A8A", TINTA = "#2B1B10";
  var COLORES = ["#D7263D", "#F4D35E", "#1B998B", "#2E86AB"];

  // La silueta del cuerpo: [x, y, k], k = 1 curva y k = 0 esquina
  var CUERPO = [[540, 800, 0], [600, 720, 1], [690, 590, 1], [660, 470, 1], [540, 430, 1], [420, 470, 1], [390, 590, 1], [480, 720, 1]];
  // La sombra propia: media luna del lado derecho, dentro del cuerpo
  var MEDIA_LUNA = [[540, 432, 0], [660, 470, 1], [690, 590, 1], [600, 720, 1], [540, 800, 0], [588, 700, 1], [624, 590, 1], [604, 482, 1]];

  app.beginUndoGroup("El trompo");

  function rgb(h) {
    return [parseInt(h.substr(1, 2), 16) / 255, parseInt(h.substr(3, 2), 16) / 255, parseInt(h.substr(5, 2), 16) / 255];
  }
  function tr(capa) { return capa.property("ADBE Transform Group"); }
  function nuevoGrupo(capa, nombre) {
    var raiz = capa.property("ADBE Root Vectors Group");
    var i = raiz.addProperty("ADBE Vector Group").propertyIndex;
    raiz.property(i).name = nombre;
    return function () { return capa.property("ADBE Root Vectors Group").property(i).property("ADBE Vectors Group"); };
  }
  function transformacion(g) { return g().parentProperty.property("ADBE Vector Transform Group"); }
  function relleno(g, color, opacidad) {
    g().addProperty("ADBE Vector Graphic - Fill");
    var f = g().property(g().numProperties);
    f.property("ADBE Vector Fill Color").setValue(rgb(color));
    if (opacidad != null) f.property("ADBE Vector Fill Opacity").setValue(opacidad);
  }
  function rect(capa, nombre, x0, y0, x1, y1, color, redondeo) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Rect");
    g().property(1).property("ADBE Vector Rect Size").setValue([x1 - x0, y1 - y0]);
    g().property(1).property("ADBE Vector Rect Position").setValue([(x0 + x1) / 2, (y0 + y1) / 2]);
    if (redondeo) g().property(1).property("ADBE Vector Rect Roundness").setValue(redondeo);
    relleno(g, color);
    return g;
  }
  function elipse(capa, nombre, cx, cy, w, h, color, opacidad) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Ellipse");
    g().property(1).property("ADBE Vector Ellipse Size").setValue([w, h]);
    relleno(g, color, opacidad);
    transformacion(g).property("ADBE Vector Position").setValue([cx, cy]);
    return g;
  }
  // Las tangentes salen de los vértices vecinos
  function trazado(puntos) {
    var n = puntos.length, v = [], ent = [], sal = [];
    for (var i = 0; i < n; i++) {
      var a = puntos[(i + n - 1) % n], b = puntos[(i + 1) % n], k = puntos[i][2] * 0.17;
      var t = [(b[0] - a[0]) * k, (b[1] - a[1]) * k];
      v.push([puntos[i][0], puntos[i][1]]); sal.push(t); ent.push([-t[0], -t[1]]);
    }
    var s = new Shape(); s.vertices = v; s.inTangents = ent; s.outTangents = sal; s.closed = true;
    return s;
  }
  function figura(capa, nombre, puntos, color, opacidad) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Group");
    g().property(1).property("ADBE Vector Shape").setValue(trazado(puntos));
    relleno(g, color, opacidad);
    return g;
  }
  function pieza(comp, nombre) {
    var capa = comp.layers.addShape();
    capa.name = nombre;
    tr(capa).property("ADBE Position").setValue([0, 0]);
    return capa;
  }

  // ── Las franjas: 320 × 400, el patrón se repite cada 320 px ──
  var franjas = app.project.items.addComp("Franjas", 320, 400, 1, T, 24);
  var capaFranjas = pieza(franjas, "Franjas");
  rect(capaFranjas, "Veta alta", 0, 140, 320, 164, VETA);
  rect(capaFranjas, "Veta baja", 0, 292, 320, 310, VETA);
  for (var i = 0; i < 8; i++) rect(capaFranjas, "Franja " + (i + 1), i * 40, 0, i * 40 + 40, 400, COLORES[i % 4]);

  var comp = app.project.items.addComp("El trompo", 1080, 1080, 1, T, 24);
  comp.bgColor = rgb(FONDO);

  comp.layers.addSolid(rgb(FONDO), "Fondo", 1080, 1080, 1, T);
  var piso = pieza(comp, "Piso");
  rect(piso, "Piso", 0, PUNTA[1], 1080, 1080, PISO);

  var sombra = comp.layers.addShape();
  sombra.name = "Sombra";
  elipse(sombra, "Sombra", 0, 0, 220, 34, TINTA, 22);
  tr(sombra).property("ADBE Position").setValue([PUNTA[0] + 16, PUNTA[1] + 4]);

  var punta = pieza(comp, "Punta");
  var gp = nuevoGrupo(punta, "Punta");
  gp().addProperty("ADBE Vector Shape - Group");
  var sp = new Shape(); sp.vertices = [[522, 796], [558, 796], [PUNTA[0], PUNTA[1]]]; sp.closed = true;
  gp().property(1).property("ADBE Vector Shape").setValue(sp);
  relleno(gp, METAL);

  // Las franjas en la principal, cubriendo el cuerpo, y la silueta justo encima
  var capa = comp.layers.add(franjas);
  capa.name = "Franjas";
  tr(capa).property("ADBE Position").setValue([540, 615]);
  var silueta = pieza(comp, "Silueta");
  figura(silueta, "Silueta", CUERPO, "#FFFFFF");

  var volumen = pieza(comp, "Sombra propia");
  figura(volumen, "Sombra propia", MEDIA_LUNA, TINTA, 28);
  var brillo = pieza(comp, "Brillo");
  var gb = elipse(brillo, "Brillo", 452, 590, 36, 150, "#FFFFFF", 40);
  transformacion(gb).property("ADBE Vector Rotation").setValue(-12);

  var cabeza = pieza(comp, "Cabeza");
  rect(cabeza, "Cabeza", 522, 392, 558, 436, MADERA, 6);

  comp.openInViewer();
  app.endUndoGroup();
})();

El orden de los efectos importa: primero se corren las franjas y después se curvan. Al revés, la curva correría con ellas y el trompo se vería plano.

Práctica 4
Práctica 4 · After Effects · 2 de 3

El tambaleo

  1. Un nulo en la punta. Capa › Nuevo › Objeto nulo Layer › New › Null Object; llamarlo «Eje» y darle Posición 540, 842. El nulo queda justo donde la punta toca el piso.
  2. Vincular el trompo. Seleccionar «Cabeza», «Brillo», «Sombra propia», «Silueta», «Franjas» y «Punta»; en la columna Parent & Link de una de ellas, elegir «Eje». Nada se mueve: las seis cuelgan del nulo.
  3. El tambaleo. En «Eje», R: 4 en el 0, −4 en el 24 y 4 en el 48; F9 en los tres y loopOut("cycle"). Se inclina a un lado y al otro, cada segundo, sobre la punta.
  4. La sombra lo sigue. En «Sombra», P: 572, 846 en el 0; 508, 846 en el 24; 572, 846 en el 48; F9 y loopOut("cycle"). La sombra se corre hacia donde se inclina el trompo.
  5. Revisar el empalme. Comparar el 191 con el 0. En los dos, el trompo inclinado 4° y las franjas en el mismo lugar.
Práctica 4
Práctica 4 · After Effects · 3 de 3

Pulir y personalizar

  1. Colores propios. Doble clic en la precomposición «Franjas» y cambiar el color de cada franja. Mientras sean ocho de 40 px, el giro sigue sin salto.
  2. Más franjas. En la precomposición «Franjas», capa «Franjas»: sin el vínculo de proporción, Escala 50, 100; Ctrl+D y, en la copia, Posición 160, 0. Dieciséis franjas de 20 px: el trompo parece girar más rápido, a la misma velocidad.
  3. Pierde fuerza y la recupera. Opcional. Seleccionar el trompo con «Eje» y «Sombra», precomponerlo con Ctrl+Mayús+C y, en esa capa, Ctrl+Alt+T. Keyframes de Time Remap, en frames: 0 en el 0, 72 en el 96 y 191 en el 191, con F9 en el de en medio. Gira más lento al principio y acelera hacia el final; el empalme sigue cerrando.
  4. Acabado. Grano, como en la primera parte. Una textura que cambia en cada frame.
  5. Exportar. Ctrl+Alt+M, H.264 o GIF animado. 192 frames; el 191 enlaza con el 0.
Práctica 5
Práctica 5 · ciclo de 1 s

El paseo

Pose a poseArcosSincronizaciónAcción complementaria

Un personaje geométrico camina en su sitio mientras el piso corre bajo sus pies.

Cuatro poses por paso: contacto, bajada, paso y subida. Un ciclo de dos pasos dura 24 frames.

◆ Apaga «Pies plantados» y mira cómo patina
000 · 00 del ciclo

Radiografía

Ver

Práctica 5
Práctica 5 · Planeación

Las cuatro poses

En el contacto los dos pies tocan el piso; en la bajada el cuerpo cae sobre la pierna de adelante; en el paso la otra pierna cruza por debajo; en la subida el cuerpo se impulsa. Después, lo mismo con el otro pie.

El pie apoyado recorre 240 px en 12 frames, a 480 px/s: la misma velocidad que el piso.

FramePosePie DPie ICadera
0Contacto660, 930420, 930540, 620
3Bajada600, 930410, 910540, 636
6Paso540, 930540, 885540, 618
9Subida480, 930610, 905540, 604
12Contacto420, 930660, 930540, 620
15Bajada410, 910600, 930540, 636
18Paso540, 885540, 930540, 618
21Subida610, 905480, 930540, 604
24Contacto660, 930420, 930540, 620

Manos: la del lado contrario al pie de adelante, adelante. Mano D 490, 640 y Mano I 600, 640 en el 0; las dos en 545, 640 en el 6 y el 18; al revés en el 12.

Práctica 5
Práctica 5 · After Effects · 1 de 3

El rig con Duik

  1. Instalar Duik Ángela, una sola vez. Se descarga de duik.org; es gratuito, con licencia GPL v3. Se abre en Ventana › Duik Ángela. Aparece el panel de Duik.
  2. Ejecutar el script 05-el-paseo.jsx y contar en frames. Se abre «El paseo»: 4 s y el personaje de pie, de perfil.
  3. Los huesos. En Duik, Bones: crear una pierna (Leg) y un brazo (Arm) para cada lado, y la columna (Spine). Mover cada hueso a su articulación: los puntos de anclaje de las piezas marcan dónde. Un esqueleto encima del personaje.
  4. Unir las piezas. Alt+clic en Link Art. Cada pieza queda unida al hueso más cercano a su punto de anclaje.
  5. Los controladores. Seleccionar todos los huesos y clic en Auto-rig. Aparecen controladores en los pies, las manos y la cadera; al mover el de un pie, la pierna se dobla sola.
05-el-paseo.jsx
// 05 · El paseo · Loops de motion design, segunda parte
// Dibuja un personaje geométrico de perfil, mirando a la derecha, de pie y
// quieto. Cada pieza es su propia capa y tiene el punto de anclaje en la
// articulación de la que cuelga: el muslo en la cadera, la pierna en la
// rodilla, el pie en el tobillo, el brazo en el hombro, el antebrazo en el codo
// y la mano en la muñeca. Así Link Art de Duik puede unir cada pieza al hueso
// más cercano. «I» es el lado izquierdo, el de atrás; «D», el de adelante.
// Las baldosas del piso se repiten cada 240 px, para correrlas mientras camina.
// No crea keyframes, expresiones ni huesos.
// Archivo › Secuencias de comandos › Ejecutar archivo de secuencias de comandos
(function () {
  var T = 4;                    // duración del loop: cuatro ciclos de 1 s
  var SUELO = 960;
  var CADERA = [540, 620], RODILLA = [552, 780], TOBILLO = [540, 930];
  var HOMBRO = [540, 440], CODO = [546, 540], MUNECA = [550, 630], CUELLO = [540, 410];
  var FONDO = "#F3F0FF", PISO = "#E4DEFA", BALDOSA = "#D6CCF7";
  var CERCA = "#3A3170", LEJOS = "#241E4E", CUERPO = "#5632DA", PIEL = "#FFC94A", ZAPATO = "#1B1B2F";

  app.beginUndoGroup("El paseo");

  var comp = app.project.items.addComp("El paseo", 1080, 1080, 1, T, 24);
  comp.bgColor = rgb(FONDO);

  function rgb(h) {
    return [parseInt(h.substr(1, 2), 16) / 255, parseInt(h.substr(3, 2), 16) / 255, parseInt(h.substr(5, 2), 16) / 255];
  }
  function tr(capa) { return capa.property("ADBE Transform Group"); }
  function nuevoGrupo(capa, nombre) {
    var raiz = capa.property("ADBE Root Vectors Group");
    var i = raiz.addProperty("ADBE Vector Group").propertyIndex;
    raiz.property(i).name = nombre;
    return function () { return capa.property("ADBE Root Vectors Group").property(i).property("ADBE Vectors Group"); };
  }
  function relleno(g, color) {
    g().addProperty("ADBE Vector Graphic - Fill");
    g().property(g().numProperties).property("ADBE Vector Fill Color").setValue(rgb(color));
  }
  function rect(capa, nombre, x0, y0, x1, y1, color, redondeo) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Rect");
    g().property(1).property("ADBE Vector Rect Size").setValue([x1 - x0, y1 - y0]);
    g().property(1).property("ADBE Vector Rect Position").setValue([(x0 + x1) / 2, (y0 + y1) / 2]);
    if (redondeo) g().property(1).property("ADBE Vector Rect Roundness").setValue(redondeo);
    relleno(g, color);
  }
  function circulo(capa, nombre, cx, cy, d, color) {
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Ellipse");
    g().property(1).property("ADBE Vector Ellipse Size").setValue([d, d]);
    g().property(1).property("ADBE Vector Ellipse Position").setValue([cx, cy]);
    relleno(g, color);
  }
  // un segmento del cuerpo: un trazo grueso con puntas redondas, desde el anclaje
  function segmento(nombre, junta, fin, grosor, color) {
    var capa = comp.layers.addShape();
    capa.name = nombre;
    var g = nuevoGrupo(capa, nombre);
    g().addProperty("ADBE Vector Shape - Group");
    var s = new Shape(); s.vertices = [[0, 0], fin]; s.closed = false;
    g().property(1).property("ADBE Vector Shape").setValue(s);
    g().addProperty("ADBE Vector Graphic - Stroke");
    var t = g().property(2);
    t.property("ADBE Vector Stroke Color").setValue(rgb(color));
    t.property("ADBE Vector Stroke Width").setValue(grosor);
    t.property("ADBE Vector Stroke Line Cap").setValue(2);
    tr(capa).property("ADBE Position").setValue(junta);
    return capa;
  }
  function pieza(nombre, junta) {
    var capa = comp.layers.addShape();
    capa.name = nombre;
    tr(capa).property("ADBE Position").setValue(junta);
    return capa;
  }
  function rel(p, junta) { return [p[0] - junta[0], p[1] - junta[1]]; }

  // ── Fondo y piso ──
  comp.layers.addSolid(rgb(FONDO), "Fondo", 1080, 1080, 1, T);
  var piso = pieza("Piso", [0, 0]);
  rect(piso, "Piso", 0, SUELO, 1080, 1080, PISO);
  var baldosas = pieza("Baldosas", [0, 0]);
  for (var i = 0; i < Math.ceil((1080 + 240) / 240); i++) rect(baldosas, "Baldosa " + (i + 1), i * 240, SUELO, i * 240 + 120, 1080, BALDOSA);

  // ── Un lado del cuerpo; primero el de atrás, luego el de adelante ──
  function lado(L, color) {
    segmento("Brazo " + L, HOMBRO, rel(CODO, HOMBRO), 34, color);
    segmento("Antebrazo " + L, CODO, rel(MUNECA, CODO), 30, color);
    var mano = pieza("Mano " + L, MUNECA);
    circulo(mano, "Mano", 0, 16, 38, PIEL);
    segmento("Muslo " + L, CADERA, rel(RODILLA, CADERA), 46, color);
    segmento("Pierna " + L, RODILLA, rel(TOBILLO, RODILLA), 40, color);
    var pie = pieza("Pie " + L, TOBILLO);
    rect(pie, "Pie", -24, -14, 76, SUELO - TOBILLO[1], ZAPATO, 16);   // la suela, a ras del suelo
  }
  lado("I", LEJOS);

  var torso = pieza("Torso", CADERA);
  rect(torso, "Torso", -50, HOMBRO[1] - 30 - CADERA[1], 50, 22, CUERPO, 46);
  var cabeza = pieza("Cabeza", CUELLO);
  circulo(cabeza, "Ojo", 36, -70, 12, ZAPATO);
  circulo(cabeza, "Cabeza", 8, -60, 120, PIEL);

  lado("D", CERCA);
  // el brazo de adelante va encima de todo
  comp.layer("Mano D").moveToBeginning();
  comp.layer("Antebrazo D").moveToBeginning();
  comp.layer("Brazo D").moveToBeginning();
  comp.layer("Mano D").moveToBeginning();

  comp.openInViewer();
  app.endUndoGroup();
})();

Link Art también puede unir por nombre, pero los nombres tienen que estar en el idioma del panel de Duik. Con Alt une por cercanía, y eso funciona con los nombres en español del script.

Práctica 5
Práctica 5 · After Effects · 2 de 3

La caminata

  1. El contacto. En el 0, keyframes de Posición en los controladores de los pies, las manos y la cadera, con los valores de la tabla. Zancada con el pie D adelante y la mano I adelante.
  2. Las demás poses. En el 3, 6, 9, 12, 15, 18 y 21, los valores de la tabla. Los keyframes de los pies, en linear: Ctrl+Alt+K, las dos interpolaciones en Linear. El pie apoyado se corre hacia atrás a velocidad constante.
  3. Cerrar el ciclo. Copiar los keyframes del 0 en el 24. F9 en los de la cadera y las manos; loopOut("cycle") en cada propiedad animada, con Editar › Copy Expression Only para repetirlo. Cuatro ciclos en 4 s.
  4. El piso. En «Baldosas», Posición: 0, 0 en el 0 y −240, 0 en el 12; loopOut("cycle"). El pie apoyado y las baldosas avanzan juntos: no patina.
  5. Comparar con Duik. Duplicar la composición en el panel Proyecto. En la copia, borrar los keyframes, seleccionar todos los controladores y aplicar Automation › Walk / Run Cycle; probar Speed y Softness. Una caminata automática para comparar con la hecha a mano.
Práctica 5
Práctica 5 · After Effects · 3 de 3

Pulir y personalizar

  1. La cabeza. Rotación de «Cabeza»: 0 en el 0, 3 en el 5 y 0 en el 12, con F9 y loopOut("cycle"). Asiente con cada bajada, un poco después que la cadera.
  2. Arcos en el pie del aire. Seleccionar los keyframes del pie que va por el aire, sin los de contacto: del 15 al 21 en el pie D y del 3 al 9 en el I. Ctrl+Alt+K › Spatial Interpolation en Auto Bezier. El pie sube y baja en curva en lugar de ir en línea recta.
  3. El pie rueda. En el controlador de cada pie, efecto Foot roll › Foot roll: negativo gira sobre el talón y positivo, sobre la punta. Pie D: −15 en el 0, 0 en el 3, 25 en el 12, 0 en el 18 y −15 en el 24; el I, 25 en el 0, 0 en el 6, −15 en el 12, 0 en el 15 y 25 en el 24. loopOut("cycle") en los dos. El pie apoya el talón y despega con la punta.
  4. Otro personaje. Redibujar las piezas dentro de sus capas sin mover sus puntos de anclaje. El rig y la caminata siguen funcionando.
  5. Exportar. Ctrl+Alt+M, H.264 o GIF animado. 96 frames: cuatro ciclos, y el 95 enlaza con el 0.
Loops
Práctica

Cómo se practica esto

Un loop propio, de 6 a 12 segundos

  • Partir de una de las cinco prácticas y cambiarle el diseño: formas, paleta y escena.
  • Sumarle la técnica de otra práctica: clones en el viaje, un morphing en el paseo o volumen en un pez.
  • Planear antes de animar: un style frame y un timing chart.
  • Decir con cuál de las cinco formas cierra el loop.
  • Exportar el ciclo exacto en MP4 cuadrado de 1080 px.

Antes de darlo por terminado

0 de 7

Loops
Cuestionario

¿Qué se rompió?

1 de 5 · aciertos 0

Cada respuesta explica por qué, acierte o no.

Loops
Referencias

Quién hace loops

  • Ben MarriottMorphing y falso 3D

    Tutoriales del proceso completo en After Effects: formas que se transforman, volumen sin capas 3D y texturas que hacen que una pieza plana parezca ilustrada.

  • Dave Whyte · Bees & BombsClones con desfase

    Una forma simple repetida muchas veces, cada copia un poco después que la anterior. El movimiento sale del desfase, no de la forma.

  • Ciclos de caminata y de carrera con personajes muy sencillos, donde el peso se nota en la cadera y en los pies.

  • El autor de Duik, la herramienta libre de rig y animación de personajes para After Effects, con su guía en línea.

Loops
En el Aula

Para seguir

Loops
Referencias

Fuentes

Adobe. (s. f.). Expression language reference. After Effects User Guide. https://helpx.adobe.com/after-effects/using/expression-language-reference.html

RxLaboratory. (s. f.). Duik Ángela user guide. https://duik.org/guide/Angela/

Thomas, F. y Johnston, O. (1981). The illusion of life: Disney animation. Abbeville Press.

Williams, R. (2009). The animator's survival kit (ed. ampliada). Faber & Faber.

Willenskomer, I. (2017, 31 de marzo). Creating usability with motion: The UX in motion manifesto. UX in Motion. https://medium.com/ux-in-motion/creating-usability-with-motion-the-ux-in-motion-manifesto-a87a4584ddc

LaboratoriosConstruidos con Canvas y JavaScript; cada uno reproduce el mecanismo de su escena de After Effects.
Scripts y expresionesEscritos para estas prácticas, en ExtendScript y en el lenguaje de expresiones de After Effects.
TipografíaPoppins, servida desde este mismo dominio.
AutoríaL.D.G. Luis E. Maldonado Gil · CodeLibri
01 / 34

Esta presentación está pensada para pantalla horizontal.