Zim ist ein Javascript Framework, welches auf CreateJS, ThreeJS,
Physics 2D und vielen weiteren Frameworks aufsetzt.
Mit Zim werden Webseiten mit dem Canvas Element
erstellt. Die moderne Alternative zu den Flash-Inhalten aus früheren Zeiten. Es bietet umfangreiche Möglichkeiten Interaktivität, Animation, Physic, 3D, Sound und vieles mehr auf der Website zu
integrieren.
Zim lässt sich auch innerhalb von Adobe Aninate
nutzen.
Zim bietet umfangreiche Tutorials, Beispiele und Video
Tipps. Diese Tipps sind nur eine klitzekleine Ergänzung zu dem umfangreichen Lernmaterial auf der
original Zim Webseite.
ZIM Tipps
ZIM Tipps eine Übersicht der ZIM Konzepte. Unter "dynamic" hat man tolle Möglichkeiten, dynamische Parameter zuzuweisen, sogenannte ZIM VEE Werte. Das geschieht beispielsweise zufällig aus einem Array oder der Reihe nach aus einer Serie.
Lerne Objekte zu positionieren und zu skalieren, Argumente zu vergeben u.v.m.
ZIM Docs Dokumentation
How to Start
Die einfachste Möglichkeit zu beginnen gehe zu ZIM Code, wähle "COPY" und füge es in eine HTML Seite ein.
Der Einbettungscode ZIM Version 17:
<import zim from "https://zimjs.org/cdn/017/zim">;
Will man weitere Framworks nutzen, wie Physics, Three Js, Pizzazz, findet man alles unter ZIM CDN Beachte, dass du 2 Bezeichner brauchst, wenn du 2 Import Befehle vergibst.
<import zim from "https://zimjs.org/cdn/016/zim_physics">;
<import zim2 from "https://zimjs.org/cdn/016/zim_pizzazz">;
ZIM mit Adobe Animate
ZIM mit Adobe Animate findet man unter ZIM
Shim mit Video und
Download
Editor
Es gibt einen ZIM online Editor. Wenn man fertig ist
kann man den Code kopieren.
ZIM History
Man findet Informationen zu den verschiedenen Versionen unter: About / Versions und Devs
Frame
Man startet mit der Definition des Frame
Die Frame Klasse hat einige Parameter für
die grundsätzlichen Einstellungen
new Frame( scaling, width, height, canvasColor, bodyColor, readyFunktion, assets, path,
sensors);
scaling
Der erste Parameter kann mit FIT, FILL, FULL , "tagId"
versehen werden, je nachdem
wie groß das Canvas Element dargestellt werden soll. siehe hier
tagId
siehe Beispiel (glump) / siehe ZIM
Video
Wenn man das Canvas Element zusammen mit anderen HTML Elementen darstellem will, gibt man als ersten
Parameter den Klassennamen eines Div Elements an
new Frame("animation",.....)
Im Body fügt man das Div und vergibt diese id. Innerhalb des div wird das Canvas Element vom Script
integriert.
<div id="animation></div>
Das canvas Element kann mit CSS versehen werden:
#animation>canvas{}
ready
Der Name einer Funktion, in welcher sämtlicher Code eingebettet ist.
sensors / Progressbar, Eieruhr, Warten-Animation
Hier kann man eine Eieruhr oder eine Fortschrittsanzeige zuweisen. In Zim gibt es dazu den
Waiter()
und ProgressBar()
.
const waiter = new Waiter({backgroundColor: blue, corner: 0});
const waiter = new ProgressBar({backgroundColor: blue});
Der Name waiter in diesem Beispiel wird dann als neunter Parameter in new Frame()
angegeben.
siehe Beispiel (glump)
KONSTANTEN
Es gibt einige Konstanten, die man im Code nutzen kann:
- F (Frame)
- S (Stage)
- W (width)
- H (height)
Grundformen und Registrierpunkt
siehe Video
In diesem Video erklärt Dan Zan wie man grafische Grundformen erstellt, den Registrierpunkt bestimmt
und
positioniert.
var tri = new Triangle(100,100, 100, blue).center();
var rec = new Rectangle(50, 50, red).pos(100, 100);
var circ = new Circle(100, violet).pos(10,20,LEFT,BOTTOM);
Registrierpunkt
Der Registrierpunkt bestimmt den Punkt, um den das Objekt gedreht oder skaliert werden
kann.
Den Registrierpunkt kann man mit regX
und regY
bestimmen. Setzt man beide
Werte auf 0 ist
der Registrierpunkt in der oberen linken Ecke.
Ein Dreieck (Triangle) soll sich um die Spitze drehen. Dazu versetzt man den Registrierpunkt
folgendermaßen.
var tri = new Triangle(100, 100, 100, blue).center();
tri.reg(CENTER, TOP);
Die Methode outline()
erzeugt ein temporäres Begrenzungsrechteck, in dem der Registrierpunkt als
kleiner Kreis
dargestellt wird.
new Rectangle().reg(RIGHT,BOTTOM).center().outline();
centerReg()
ZIM centerReg() setzt den Registierpunkt und das Objekt in die Mitte. 2 Fliegen mit einer Methode. Wenn das Objekt sich in einem Container befindet wird es in dem Container zentriert. Befindet es sich nicht in einem Container wird es im Frame zentriert.
new Circle().reg(200, 200).centerReg();
Position
Es gibt viele Möglichkeiten Objekte zu positionieren siehe ZIM Tipps
siehe ZIM Tipps Position
Farben & Füllung
Man kann alle CSS Farben in Anführungszeichen setzen. Außerdem gibt es einige ZIM Farben Konstanten und man kann diesen Farbkonstanten Attribute anhängen green.dark
. Desweiteren gibt es Methoden, die Farben zu verändern: green.darken(.5);
colorRange()
Mit der Methode colorRange kann man mehrere Farben erzeugen, die zwischen 2 Farben liegen. colorRange(color1, color2, ratio)
ratio ist ein Wert von 0 bis 1, wobei 0 die erste Farbe ist und 1 die zweite oder letzte und 0.5 genau in der Mitte zwischen beiden Farben liegt.
Farbverläufe
siehe Beispiel(fill) / etwas veraltetes Video
Man kann gerade und kreisförmige Verläufe definieren und außerdem eine Bitmapfüllung erzeugen. GradientColor, RadialColor(), BitmapColor()
GradientColor
Die Anzahl und Reihenfolge der Argumente können variieren.
Kurzschreibweise, ein linearer Verlauf von gelb , rot, schwarz in gleichmäßiger Verteilung und 110 Grad Drehung.
new GradientColor([ yellow, red, black ], 110)
Ein Verlauf von gelb, schwarz, rot. Die Farbe gelb liegt auf 30 Prozent. Die Farbe schwarz auf 50 Prozent und die Farbe rot auch auf 50 Prozent. Der Verlauf ist um 45 Grad gedreht.
new GradientColor( [ yellow, black, red ], [ .3, .5, .5 ], 45 )
Der nächste Verlauf, geht von gelb nach schwarz. Gelb startet auf Position 90 Prozent, schwarz auf 100 Prozent. Die 4 Parameter nach dem zweiten, sind 2 Punkte mit x-y Posiitionen und bestimmen die Linie des Verlaufs anstatt eines Winkels. In diesem Beispiel startet der Verlauf links, oben und endet auf links und 200 Pixel unten.
GradientColor([farbe1, farbe2],[prozent, prozent], xStart, yStart, xEnde, yEnde
)
new GradientColor( [ yellow, black ], [.9,1], 0,0, 0,200 )
BitmapColor
Beispiel (fill)
Immer wenn man Bitmaps einbindet, müssen diese vorher geladen werden siehe Assets. Dann kann man anstatt eines Farbarguments eine BitmapColor zuweisen.
new BitmapColor("knop.jpg")
RadialColor
Beispiel (fill)
RadialColor funktioniert ähnlich wie GradientColor nur hier werden die Farben auf dem Radius definiert, also vom Mittelpunkt bis außen. Man kann auch hier die Positionswerte der Farben angeben.
Desweiteren kann man optional 2 Mittelpunkte definieren
Dann wirds kompliziert. Mann kann 2 einen Mittelpunkte angeben mit x, y und jeweils einen Wert der den Start des Radius angibt und das Ende des Radius. Im folgendem Beispiel ist es ein Rechteck von 200 x 200. Der Mittelpunkt der schwarzen Farbe liegt oben in der Mitte. 0,100, Der Start des Radius ist 50. Der zweite Mittelpunkt liegt oben links 0, 0 und der Endradius bei 200. Die dritte Farbe liegt außen auf 1. Dadurch kann man schön erkennen, dass die zweite Farbe weiß von der linken oberen Ecke, bis zur Außenkante 200 verläuft.
RadialColor( [ black, "white", "##ffaaffaa" ], [ 0, 1 ,1], 100, 0, 50, 0, 0, 200 )
RadialColor([farbe1, farbe2, farbe3],[pos1, pos2, pos3], x, y, Startradius, x, y, Endradius)
Components
ZIM stellt eine Reihe von Komponenten zur Verfügung wie Buttons, Beschriftungen/ Labels, Schieberegler/
Sliders,
Auswahlfelder und viele mehr.
siehe Video
Buttons & Labels
Ein Button ist schnell und einfach erstellt. Die Parameter sind Breite, Höhe, Beschriftung
var button = new Button(200,60,"CLICK");
button.center();
button.on("click", function(){zog("clicking");});
Hier ein etwas ausführlicher Button. Man erzeugt zuerst ein Label und weist dieses dem Button zu.
Ein Label kann man auch als Text der Stage hinzufügen.
var label = new Label({
text: "click",
size: 20,
color: "white",
rollColor:"black",
fontOptions: "bold"
});
var button = new Button({
label: label,
width: 70,
height: 35,
backgroundColor: "purple",
rollBackgroundColor:"MediumOrchid",
borderWidth: 3,
borderColor: "violet",
corner: 15
});
Duplikate
Objekte lassen sich einfach duplizieren. Das folgende Beispiel erzeugt ein Duplikat des zuvor erzeugten
Buttons.
var button1 = new Button(100, 50, "Click").center;
var button2 = button1.duplicate();
button2.pos(100, 20, RIGHT, BOTTOM);
button2.label.text = "hallo";
button2.on("click", function() {
zog("button2 angeklickt");
});
Blob
ZIM Docs
Ein Blob ist eine Vektorgrafik mit Anfassern an den Punkten, die der User bedienen kann. Die Punkte lassen sich ausgeben, speichern und zweisen.
Im Beispiel kann man die Punkte selber setzen, abfragen und zuweisen. Außerdem kann man die angezeigten Punkte nutzen, um sie dem point Parameter eines Blobs zuzuweisen.
new Blob({points: [[-345.1,36.3,0,0,-149.4,-22.6,49.4,7.5],[-279.5,119.4,0,0,0,0,0,0,"none"],[-463.4,97.7,0,0,0,0,0,0,"none"]]})
Man kann einem Blob addPhysics()
zuweisen, dann wird die Form automatisch die physikalische Form. Aber die Punkte müssen im Uhrzeigersinn angeordnet sein. Wenn es nicht klappen sollte setze myBlob.reversePoints()
Siehe auch png2blob
Assets
Assets sind Bilder oder Sounds, welche vorausgeladen werden, um sie dann in Canvas Element integrieren zu
können.
In der Konstruktormethode der Frame Klasse, kann man die Werte für das Vorausladen der Elemente einfügen.
Die Parameter der Frame Konstruktormethode sind:
new Frame( scaling, width, height, canvasColor, bodyColor, readyFunktion, assets, path,
sensors);
Nun erzeugt man ein Array für die assets und eine Url für den Pfad und gibt beides dann in dern Frame
Funktion ein.
const assets = [ { id: 'robo', src: 'spaceguy.png' }, { id: 'roboJs', src: 'spaceguy.json' } ];
const path = "../spriteSheet/assets/";
new Frame( FIT, 1024, 768, black, darker, ready, assets, path );
Hier ein paar Möglichkeiten das Bitamap zuzuweisen.
var robbi = new Pic( "robo" ).centerReg();
var rad = asset("rad.png").centerReg();
var radel = asset("rad.png").addPhysics({shape: circle});
Es reicht auch, nur die Namen der Dateien einzugeben.
const assets = ['spaceguy.png', 'spaceguy.json', 'bum.mp3']
Sprite
siehe ZIM Sprite
/ siehe Video
Dr Abstract /
Ein Sprite ist ein Bild welches aus vielen Einzelbildern besteht, die in Reihen und Spalten aufgeteilt
sind. Diese Einzelbilder kann man als Animation abspielen, oder man zeigt nur eins der Einzelbilder an,
beispielsweise das Bild eines Kartenspiels oder Memory-Spiels.
Spritesheets kann man erstellen mit Texture Packer, Adobe Animate oder Sprite Sheet
Packer
const robbi = new Sprite( {
image: "robo",
cols: 6,
rows: 5,
animations: { mid: [ "0", "10" ], end: [ "20", "30" ] }
} )
.pos( -200, 0, LEFT, BOTTOM )
.run( { time: 1.4, loop: true, label:"mid" } )
.animate( {
props: { x: W },//W Breite der Bühne
time: 6,
ease: "linear",
loop: true,
loopWait: 1 //am Ende des loops Wartezeit 1 Sekunde
} );
Die Parameter der Sprite() Funktion sind Bildname aus dem Asset, Spalten und Reihen. Der Parameter
animations, bietet die Möglichkeit einen Teilbereich für eine Animation zu bestimmen. Hierbei gibt man
Startframe und Endframe, des Animationsausschnitt an und weist sie einem selbstvergebenen Labelnamen zu.
mid:["13","20"]
In der run Methode kann man diesen Namen unter label angeben:
.run({time: 2, label:"mid", loop:true})
Beachte das funktioniert nur ohne Json.
Json
So ein Spritesheet kann auch eine Json Datei haben, mit Informationen über die Spalten und Reihen,
Labels, Geschwindigkeit.
Eine solche Json-Datei kann man in den Assets einfügen, {id: 'lampJs', scr:lampi.json}
und
sie dann in der Konstruktorfunktion des Spritesheets zuweisen. Auf rows
und cols
kann man dann verzichten.
const lamp = new Sprite({
image: "lampi",
json: 'lampJson',
}).run({loop:true});
Möchte man nur einen Teilbereich animieren wie oben erklärt, gibt man diesen Teilbereich in der Json
Datei ein. siehe diese Json-Datei unten. siehe dieses Beispiel (glump)
Je nach Art des Json-Formats kommt dieser Bereich an eine bestimmte Position. Siehe dazu dieses Video
"animations": {
"walk": [0, 16],
"cry": [17, 24]
}
Beispiel (glump)
Dynamo
ZIM Doc / Beispiel dynamo / Beispiel dynamo2
Die Klasse Dynamo bietet eine Alternative zur run() Methode eines Sprites. Man kann damit definieren,
welche Frames, wie schnell abgespielt werden. Man kann eine prozentuale Geschwindigkeit definieren. Man kann das Ende jeder Schleife abfangen.
reversible: false bedeutet, dass bei negativem speed, das Objekt gespiegelt wird.
In folgendem Beispiel bestimmt die Mausposition die Abspielgeschwindigkeit. Sie wird der
Dynamo-Eigenschaft percentSpeed
zugewiesen. Alternativ gibt es auch die Methode
speed
das bedeutet frames pro Sekunde.
const glump = new Sprite( {
image: "glump",
json: "glumpJs"
} ).sca( -1, 1 ).centerReg();
const dynamo = new Dynamo( { sprite: glump, speed: 30, label: "walk", reversible: false } );
Ticker.add( () =>
{
dynamo.percentSpeed = ( ( F.mouseX - W / 2 ) / ( W / 2 ) * 200 );
} );
Die Dynamo Methode pause(), pausiert den Dynamo, mit pause(false) kann man die Pause beenden. Im Beispiel2 gibt es auf Pfeiltaste nach oben eine Pause von 1 Sekunde.
Während der dynamo pausiert wird, wird die run() Methode des sprites aufgerufen. Dort kann man mit call
eine Callback Methode zuweisen, die aufgerufen wird, wenn die run() Methode beendet ist. Eine coole
Möglichkeit eine andere Sequenz des Sprites abzuspielen, beispielsweise einen Sprung, einen Schrei oder
Schussbewegung.
F.on( "keydown", e =>
{
if ( e.keyCode == 38 )
{
glump.impulse( 0, 400 );
dynamo.pause( true );
glump.run(
{
label: "cry",
time: 1.5,
loop: false,
call: () => { dynamo.pause( false ) }
}
);
}
Keyboard / Tastenanschlag
Folgendermaßen kann man auf die Tastatur zugreifen. F ist die Konstante für frame und diese
hat den Event keydown
. Über den Event-Parameter wird über key
ein String der
Taste geliefert und über keyCode
, der Keycode. Die Eventeigenschaften altKey, ctrlKey,
metaKey
liefern den boolschen Wert true, wenn sie zusätzlich gedrückt wurden.
F.on( "keydown", e =>
{
zog( e.key );
zog( e.keyCode );
zog( F.altKey );
} );
ZIM Doc / Beispiel 1 / Beispiel2
Im Beispiel 1 wird ein Tile erstellt und dieses als Scroller verwendet. Das Tile
wird mit einer definierten Geschwindigkeit gescrollt. Der Parameter gapFix
definiert einen
Überlappungswert am Übergang, wo Ende und Anfang aneinander stoßen. Sollte eine dünne Linie enstehen, ändere diesen Wert.
Achtung der Inhalt des Scrollers sollte den Registrierpunkt nicht in der Mitte haben. Den Scroller kann man auch mit einen Accelerator verbinden oder einem MotionController oder mittels Tasten steuern. siehe hier
const rect = new Rectangle( {
width:30,
height:30,
color:dark,
corner:30}
const background = new Tile(rect), 40, 7, 5, 5).alp(.3).center();
const scrolli = new Scroller( { backing: background, speed: 1.5, gapFix: -5 } );
MotionController
ZIM Doc / Zim Beispiel / Zim Video
Will man Objekte per mousedown, mousemov e, keydown, gamebutton, gamestick, swipe bewegen, setzt man die
Klasse MotionController ein.
Beispiel keydown / Beispiel mousedown & mousemove
keydown
Beispiel keydown siehe Developer
Als ich damals mit Flash programmierte, habe ich festgestellt, dass Bewegungssteuerung mit Tastatur nicht einfach ist. In ZIM wurden diese Probleme sehr gut gelöst.
Man erzeugt ein Objekt und weist dieses dem Motioncontroller mittels target Parameter zu. Welche Taste
gedrückt wird, definiert man mittels map Parameter. Vergibt man den Map Parameter nicht, sind es die Standardtasten: Pfeilstasten, ADWS.
Im map Parameter gibt man ein Array mit Keycodes ein. [links, rechts,oben, unten]
Will man für
eine Richtung 2 oder mehr Tasten bestimmen, wird auch das mittels Arrayschreibweise eingegeben [45, 56]
. Im Beispiel finden wir den
Keycode für die Tasten ADWS und zusätzlich die Pfeiltasten Zum Beispiel die Taste für linke Richtung ist [65, 37]
65=A , 37 = linkePfeiltaste. Wie oben erwähnt müssen diese Standardtasten nicht eingeben werden.
const controller = new MotionController( {
target: car,
type: "keydown",
diagonal: true,
damp: .2, //Dämpfung bei der Drehung
rotate: true,
map: [ [ 65, 37 ], [ 68, 39 ], [ 87, 38 ], [ 83, 40 ] ],
speed: 10
} );
firstPerson
Der Parameter firstPerson:true
erzeugt eine andere Art von Steuerung, bei der die links und rechts Taste für die Drehung zuständig ist, die vertikalen Tasten für die Bewegung.
Siehe Beispiel MotionControl nutze Pfeiltasten
boundary
In dem Beispiel wurde auch der Parameter boundary genutzt. Die Bewegung findet nur innerhalb dieser Begrenzung statt. Beachte im Beispiel MotionControl wie cool sich das Objekt an der Grenze verhält.
boundary: new Boundary(x,y,width, height)
Es folgt ein Script, welches key
(String) und keyCode
(numerisch) in der
Konsole anzeigt. zog()
ist die Zimschreibweise für console.log()
F.on( "keydown", e =>
{
zog( e.key ) // a string
zog( e.keyCode ) // the numeric code
zog( F.altKey ) // true if alt is pressed - also: ctrlKey, metaKey,
} );
mousedown
mousedown
ist der Standard type. Es funktioniert nur, wenn man direkt auf die Bühne klickt und nicht auf Objekten, die sich auf der Bühne befinden. Die Objekte, die auch auf mousedown
reagieren sollen fügt man unter mousedownIncludes
in einem Array ein. Nehmen wir an in folgendem Beispiel gibt es ein Hintergrundbild namens backing.
const controlli = new MotionController(
{
target: rect,
type: "mousedown",
speed: 5,
damp: .5,
rotate: true,
diagonal: true,
mousedownIncludes:[backing]
}
);
mousemove
const controller = new MotionController( {
target: circle,
type: "mousemove",
speed: 10
} );
Accelerator
ZIM Doc / Beispiel
Ein Accelerator ist ein Beschleuniger mit dem man die Geschwindigkeit mehrerer Animationen steuern kann.
Man erzeugt einen Accelerator und weist diesem gleich in der Konstruktorfunktion oder über add() mehrere
Animationen zu, beispielsweise Scroller oder Dynamo
const accelerator = new Accelerator().add( [ scro1, scro2, scro3 ] );
oder
const accelerator = new Accelerator([ scro1, scro2, scro3 ] );
Über einen MotionController kann man eine Geschwindigkeit anhand der Mausbewegung, Mausposition,
Tastaturbefehl etc. definieren. Als target wird der Accelerator angegeben. So werden die Geschwindigkeiten
prozentual verändert.
const scro1 = new Scroller( new Pic( "fensterBack.jpg" ).centerReg(), 1 );
const scro2 = new Scroller( new Pic( "schaltBack.jpg" ).centerReg(), 2 );
const scro3 = new Scroller( new Pic( "foreground.png" ).centerReg(), 3 );
const accelerator = new Accelerator( [ scro1, scro2, scro3 ] );
const mc = new MotionController( {
target: accelerator,
type: "mousemove",
axis: "horizontal",
speed: 100,
minPercentSpeed: -200,
maxPercentSpeed: 200
} );
Beispiel Scroller / Beispiel Animate
Animate
siehe ZIM Video / ZIM Doc / ZIM Video Animate Doc / ZIM Animate Übersicht
Es gibt in ZIM eine komfortable Animation, mit der man die Eigenschaften von Objekten animieren kann.
siehe Beispiel Animate1
const circle = new Circle(100, pink).center().drag();
circle.animate({
props:{scale:2, x:"-200"},
time:2,
loop:true,
rewind:true});
Einige wichtigeParameter:
time: 2.5
definiert die Länge der Animation mit einer Fließkommazahl in Sekunden.
Unter props
ist ein Animationobjekt für die Eigenschaften. Siehe im Beispiel x:"-200"
Der Wert in Anführungstrichen definiert einen relativen Wert zur Position. Ohne Anführungsstriche ist ein absoluter Wert.
Im obigen Beispiel wird die Größe geändert, durch rewind:true
, ändert sich die Größe vor und zurück.
rewindWait:2
erzeugt vor jedem Richtungswechsel eine Wartezeit von 2 Sekunden.
Man kann mehrere call Parameter setzen, denen man eine Funktion zuweist. Der Parameter in der Funktion verweist auf das animierte Objekt. Im folgendem Beispiel wird am Ende der Animation das Objekt um 20 Grad gedreht.
call: target=>{target.rotation += 20;}
rewindCall: ()=>{zog("und zurück")}
ruft eine Funktion nach dem Richtungswechsel auf. Hat man einen rewindWait
angegeben, wird die Funktion nach der Wartezeit aufgerufen.
rewindWaitCall: ()=>{zog("Moment")}
ruft die Funktion vor der Wartezeit auf.
loop:true
erzeugt eine Dauerschleife, setzt man zusätzlich loopCount:2
ein, gibt es nur 2 Schleifen.
loopWait: 2
erzeugt nach jeder Schleife eine Wartezeit von 2 Sekunden.
Mit loopCall
, wird nach jeder Schleife eine Funktion aufgerufen. Auch hier gibt es noch loopWaitCall
, die Funktion wird vor der Wartezeit aufgerufen.
loopCall: (target)=>{target.rotation += 20;}
ease:backOut
Es gibt viele Arten Beschleunigungen auch easing genannt. Standard ist "quadInOut"
"bounceOut", "elasticIn", "backInOut", "linear", "snapIn", "snapOut", "snapInOut", "ballisticIn", "ballisticOut", "ballisticInOut", "slowmoIn", "slowmoOut", "slowmoInOut"
Unter ZIM Ease hat man sogar die Möglichkeit eigene Easings mittels Editor zu erstellen.
Die drag()
Methode stoppt die Animation automatisch. Dieses kann man folgendermaßen verhindern:
drag({removeTweens:false})
Series
Eine Serie bedeutet man definiert mehrere Eigenschaften, die nacheinander animiert werden. Das geschieht indem man mehrere Parameter in einem Array aufführt.
rect.animate(
[{props:{x:"100"}, time:1},
{props: {y:"100"},time:1} ],
);
Wenn man diese Serie der aufeinanderfolgender Animationen insgesamt in einer Schleife abspielen will und dieser Gesamtanimation noch weitere Parameter zuweisen will, geht man folgendermaßen vor:
rect.animate(
{
props: [ { props: { x: "100" }, time: 1 },
{ props: { y: "100" }, time: 1 } ],
loop: true,
ease: "linear"
} );
from
Anstatt einen Wert zu definieren, zu dem hin animiert wird, kann man auch einen Wert bestimmen von dem aus animiert wird und zwar zu der Position (oder anderen Eigenschaft), die dem Objekt zuvor zugewiesen wurde.
Dazu setzt man from:true
Nehmen wir an das Objekt liegt in der Mitte der Bühne objekt.centerReg()
. Im folgenden Beispiel beginnt die Animation bei y: -100
rect.animate({props: {y: -100}, from:true, time:2})
Animation per Mausklick stoppen und starten
Wir haben ein Objekt circle mit einer Animation.
S.on( "stagemousedown", () =>
{
circle.pauseAnimate( !circle.paused );
} );
Der boolsche Wert der Eigenschaft paused
, wird bei jedem Klick umgekehrt.
Verschiedene Animationen pausieren
Wenn man verschiedene Animationen pausieren und laufen lassen will, muss man in den Animationen dem id Parameter einen selbstvergebenen Wert zuweisen. Der Name kann öfter vergeben werden. Dann kann man diesen Wert in pauseAnimation(true, "idName")
zuweisen, damit alle Animationen mit der id angesprochen werden.
circle.animate( {
props: { x: 200 },
rewind: true,
time: 1,
loop: true,
id: "hinHer"
} );
pauseAnimate( true, "hinHer" );
Animationen an einem Pfad / Squiggle
Beispiel 3
Man erstellt einen Pfad und weist diesem dem props: Parameter zu. props: {path:squig}
const squiggle = new Squiggle().addTo();
const tri = new Triangle().addTo();
tri.animate( {
props: { path: squiggle },
} );
Mehrere Geschwindigkeiten synchronisieren
Mit dem Accelerator und MotionControl kann man die Animationen mehrerer Animationen synchronisieren. Der MotionController
bietet die Möglichkeit eine prozentuale Geschwindigkeit zu definieren. Dazu muss in der Animation der Parameter dynamic:true
gesetzt sein.
Siehe Beispiel Animate1
sequence
Im nächsten Beispiel Tile wird der Parameter sequence
der Animaion eingesetzt. Dadurch werden alle Objekte eines Tile Objekts oder Arrays nacheinander animiert.
Siehe Beispiel Tile
Tile
ZIM Video / ZIM Doc
Mit der Tile Klasse kann man Objekte kacheln. Die ersten Parameter der Konstruktorfunktion sind:
new Tile(Objekt, Spalten, Reihen, horz Abstand, vert Abstand)
const circle = new Circle( 40, pink ).centerReg();
const tile = new Tile(circle,8,5).center();
Weist man einem Tile eine Animation zu, so wirkt sich das auf das komplette Tile aus. Der Animation Parameter sequence: 1 mit einem Zeitwert in Sekunden, weist die Animation jedem einzelnen Kindobjekt nacheinander zu. Der Zeitwert bestimmt den Zeitraum zum Start der Animation des nächsten Objekts.
Beispiel Tile
const tile = new Tile( circle, 8, 5 ).centerReg().animate( {
props: { scale: .5 },
time: 1,
sequence: .2
} );
Tile.drag()
Die drag() Methode dem Tile zugewiesen, bewirkt, dass jedes Kindobjekt verschoben wird. Will man das ganze Tile verschieben, setzt man folgenden Parameter:
drag({currentTarget:true});
loop()
ZIM Video / siehe ZIM Doc
Die Zim loop() Funktion ist eine Schleife für viele Gelegenheiten. Sie kann for-Schleifen und for-in Schleifen und vieles mehr ersetzten
Die Zim Methode loop() lässt sich auf einen Container, ein Array, ein Objektliteral anwenden.
loop(myArray, funktion, richtung);
Eine andere Möglichkeit ist, man lässt die Schleife mit einer vergebenen Anzahl durchlaufen.
loop(50, funktion());
Schleife rückwärts
Wenn man die Schleife rückwärts ausführen will, setzt man den Parameter dreverse auf true. Das ist wichtig, wenn man die Kindelemente aus einem Container oder Array entfernt. Das hängt mit dem Index zusammen. Man löscht von hinten nach vorne, sonst müsste man alle nachfolgenden Indizies ändern.
loop(obj, funktion, true);
Beispiel loop
loop(30)
Im nächsten Beispiel ist der erste Paramter ist die Anzahl der Schleifendurchläufe, der zweite Parameter ist ein Funktionsaufruf, dessen erster Parameter der Index ist.
loop(5,(i)=>{
new Circle( 20, purple )
.pos(i*60, 30);
});
loop(Array)
Wenn es ein Array ist, ist der erste Parameter der call Funktion das Element des Arrays. Im Beispiel wird das Array rückwärtig durchlaufen, reverse:true
der dritte Parameter
const tiere = [ "Löwe", "Affe", "Giraffe", "Elefant", "Nashorn" ];
loop( tiere, ( el, i ) =>
{
zog( el + i); //Nashorn4, Elefant3, etc.
}, true );
Die Parameter der Callback Funktion (nicht der loop() Funktion) sind:
loop(element, currentIndex, totalLoop, startIndex, endIndex)
loop(Object)
Wenn man durch ein Object-Literal loop sind die Parameter der Callbackfunktion loop(objekt, funktion(Eigenschaft, Wert, currentIndex, totalLoops, startIndex, endIndex))
Im folgendem Beispiel wird davon ausgegangen, dass es ein Lable() namens anzeige gibt.
const person = {
name :"Michael Albers",
beruf:"Coder, Artist",
ort:"Dortmund",
adresse:"Brackeler Hellweg"
}
loop(person,(prop,val)=>{
anzeige.text += prop + " : "+val+"\n";
});
loop(String)
Die einzelnen Zeichen eines Strings werden durchlaufen. Das folgende Beispiel setzt in label namens anzeige voraus:
const lied = "Alle meine Enten schwimmen auf dem See.";
loop(lied, e=>{anzeige.text += "-"+e});
loop(Container)
Bei einem Container wie beispielsweise Tile durchläuft die Callbackfunktion die Kindelemente.
loop(myContainer, child=>{} );
Es gibt auch noch die Methode loop() des Tiles
tile.loop(child=>{})
Beispiel tile2
const circle = new Circle( 40, pink ).centerReg();
const tile = new Tile( circle, 8, 5 ).centerReg();
tile.loop( function ( circ, i )
{
circ.radius = i * 1.5;
circ.color = colorRange( "#ffff00", "#00ffff", i / ( tile.cols * tile.rows ) );
} )
Man beachte die tolle Funktion colorRange()
. Sie erwartet 2 Farben und erzeugt alle möglichen Farben die dazwischen liegen. An welcher Stelle, wird über den dritten Parameter festgelegt mit einer Zahl von 0 bis 1. Dabei ist .5 die Farbe genau inder Mitte. In diesem Beispiel wird der Index durch die Anzahl der Kindelemente geteilt, um von 0 bis 1 vom ersten bis zum letzten zu gelangen.
Achtung Beachte auf folgende Besonderheit bei Tiles(). tile.items
Physics
ZIM Video / ZIM Docs / alles über ZIM Physics / npmjs.com Physics
einige meiner Beispiele Physics
siehe Beispiel 1 / siehe Beispiel 2 / Beispiel 3 / Beispiel 4 / Beispiel 5 / Beispiel Billard
Zim setzt auf die Box 2D Physic Engine auf und erleichtert den Umgang und Einstieg damit. Nutze den Einbettungscode: <import zim from "https://zimjs.org/cdn/016/zim_physics">;
Außerdem gibt es die Methode addPhysics(), welche man Displayobjekten zuweisen kann. Die Objekte sollten den Registierpunkt in der Mitte haben centerReg() sich auf der Bühne oder in einem nicht transformierten Container in 0,0 befinden.
new Circle().centerReg().addPhysics()
;
siehe Beispiel 1
new Physics()
ZIM Doc
Die Physics Klasse, ist ein Wrapper für die Box2D engine. Hier kann man generelle Einstellungen vornehmen und Methoden hinzufügen.
Der erste Parameter Schwerkraft ist standardmäßig 10. Setzt sie auf 0 ergibt das keine Schwerkraft, Vogelperspektive, wie beim Billard.
new Physics(0)
Der zweite Paramter erwartet ein Objekt der Boundary Klasse new Boundary(x,y,width, height)
siehe auch boundary für Jump
siehe Beispiel 2
Hängt man an die Physics Klasse .drag()
werden alle Objekte verschiebbar . Es ist eine andere Art von drag() als üblich. new Physics().drag();
Man kann drag()
ein Array von Objekten übergeben, dann sind nur diese Objekte verschiebbar. drag([obj1, obj2])
Diese Objekte müssen den Standardwert dynamic: true bekommen
obj.addPhyics({dynamic:true})
new Physics().debug()
zeigt Anfasser und Physic Objektausmaße.
addPhysics()
Alle Objekte, die pyhsikalische Eigenschaften bekommen sollen, werden mit addPhysics()
versehen. Hier gibt es einige Parameter.
Der erste Parameter dynamic: true
bewirkt auf false gesetzt, dass das Objekt zwar pyhsikalisch ist, aber statisch fest verankert ohne Drehung und und Bewegung.
Parameter shape
definiert die Form: "rectangle", "circle", "triangle"
, Der Standardwert ist "rectangle" Es lassen sich auch andere Formen definieren und man kann auch einem Blob addPhysics zuweisen und es ist sogar möglich die Form eines png mit Transparenz programmtisch in einen Blob zu konvertieren. siehe png 2 Blob
Parameter linear : 5
bewirkt eine Dämpfung der Bewegung. friction:8
ist Reibung, diese wirkt nicht bei rollendem Circle. bounciness:0
ist Abprall. Ist derWert über 1, ist der Abprall größer als der Aufprall. angular: 5
ist die Dämpfung der Drehbewegung.
follow()
.follow()
wird eingesetzt wenn die boundary der Physic größer als das Fenster ist und die Ansicht dem Objekt folgen soll. Dazu braucht die Klasse Physics den Parameter scroll: true Das macht nur Sinn, wenn man das auch sieht beispielweise mit einem Hintergrundbild oder Muster im Hintergrund.
const physics = new Physics( {
borders: new Boundary( 0, -H, S.width*2, S.height *2 ),
scroll: true } ).drag();
new Circle( 40, red ).centerReg().addPhysics().follow();
Objekt positionieren
Will man ein physikalisches Objekt positionieren, geht das nicht mit den üblichen Methoden wie, pos(), mov() loc(). Stattdessen weist man der body Eigenschaft x und y Werte zu. Nehmen wir an, es gäbe ein Objekt namens rect.
rect.body.x = 200;
rect.body.y = 500;
siehe Beispiel 2 / Beispiel 3 / Beispiel 4
Es gibt Methoden wie force(), impulse() spin()
um die Objekte zu bewegen. Es ist keine Bewegung im eigentlichen Sinne, sondern es sind Kräfte die auf ein Objekt einwirken.
Beispiel Billard
control Steuerung per Tastatur
siehe Beispiel 5
Durch die Methode .control()
kann man ein Objekt per Pfeiltasten und wasd Tasten bewegen. Der Parameter speed:50
definiert die Geschwindigkeit.
rad.control({speed:50});
Die Parameter sind:
control(type, speed, speedY, horizontal, vertical)
type
Standard "both" oder setze "wasd" oder "arrows" oder ein ZIM DPad Objekt.
Man kann auch die vertikale Geschwindigkeit setzen mittels speedY:0
Außerdem gibt es die Parameter vertical
und horizontal
mit Standardwert true
Die Tastatur funktioniert jedoch nur, wenn sie mit der Maus angeklickt wurde. deswegen wird ein Pane erzeugt, welches in der show() Funktion die control() Methode zuweist. Die show() Funktion blendet das Pane aus.
new Pane( "Start", black, yellow, W ).show( () =>
{
rad.control( { speed: 80 } )
} );
Jump
siehe Beispiel 5
Das Springen ist etwas kompliziert. Generell wird ein impulse() vertical gesetzt. Das soll aber nur geschehen, wenn das Objekt ein anderes Objekt berührt. So wird verhindert dass der Befehll ständig wiederholg wird. Daher wird eine Variable beispielsweise ground angehängt, welche durch die Methode contact auf true gesetzt wird. contact() wird aufgerufen, wenn eine Berührung stattfindet. Beim Sprung wird ground auf false gesetzt. Der Sprung wird durch die Leertaste, W-Taste, Arrowup Taste ausgelöst
rad.contact( obj =>
{
rad.ground = true;
} );
const down = F.on( "keydown", e =>
{
if ( rad.ground && ( e.key == "ArrowUp" || e.key == "w" || e.key == " " ) )
{
rad.ground = false;
rad.impulse( 0, -300 );
}
} );
boundary Deckenhöhe verdoppeln
Beachte im Quelltext des Beispiels, dass die boundary geändert wurde. Die Höhe ist nun 2 mal so hoch wie die Bühne H*2 und die vertikale Position ist -H also unten anstatt oben. Dadurch wird die boundary nach oben höher, so dass das rad nicht an die Decke stoßen kann.
new Boundary( 0, -H, 5300, H*2 )
Decke oder andere Seite ignorieren
Wenn das Objekt nicht an irgendeine Seite des pyhsikalischen Raums anstoßen soll, so kann man diese entfernen.
physics.remove(physics.borderTop)
Aber Vorsicht man kann auch schnell außerhalb des physikalischen Raums liegen und kommt dann nicht wieder rein, weil man das Objekt zuerst nach oben und dann über den linken oder rechten Rand hinaus schießt.
Oder man entfernt den Boden und das Objekt fällt in die bodenlose Tiefe.
physics.remove(physics.borderBottom);
contact()
Mittels contact() kann man ermitteln, ob und welches Objekt berührt wurde. Der erste Parameter einer Callback Funktion definiert das berührte Objekt. Der zweite den body, des berührten Objekts, der dritte, das Objekt, dem die contact() Methode zugewiesen wurde.
ball.contact( obj =>
{
var colorNow = obj.color;
obj.color = black;
timeout( .3, () =>
{
obj.color = colorNow;
} );
} );
Im Beispiel wird das Objekt, welches von Objekt ball berührt wurde schwarz eingefärbt und nach einer drittel Sekunde wieder auf die ursprüngliche Farbe gesetzt. Das Problem ist nur, wenn das Objekt nochmal berührt wird, bevor es wieder seine ursprüngliche Farbe bekommen hat, bleibt es schwarz, weil die colorNow bei Berührung schwarz ist. Daher ist folgende Lösung besser:
Billard
ball.contact( ( obj ) =>
{
if ( obj.color != black )
{
var aktColor = obj.color;
obj.color = black;
timeout( 0.3, () =>
{
obj.color = aktColor;
} );
};
} );
Oder noch besser wäre man färbt das Objekt wieder um, wenn der Contact beendet wurde mit contactEnd()
wie im folgendem Beispiel
In dem Beispiel wird außerdem ein Objekt erstellt, welches mittels sensor:true
einem addPhysics Parameter, das berührende Objekt zwar registiert, aber nicht auf die Berührung physikalisch reagiert.
Außerdem stößt der Ball nicht an die Decke an, weil diese folgendermaßen entfernt wurde: (siehe oben)
physics.remove( physics.borderTop );
Möchte man Objekte einer bestimmten Gruppe ermitteln, so gibt man den Objekten eine Eigenschaft mit einem String und fragt diese ab.
myRec.typus = "enemy";
myCirc.typus = "enemy";
ball.contact( ( obj ) =>{
if(obj.typus == "enemy"){
obj.body.rotation = 30;
}
});
Png 2 Blob
siehe Video / siehe Beispiel
Png Bilder können Transparenz besitzen. Die mit sichtbaren Pixeln gefüllte Form kann man programmatisch in einen Blob (eine Vektorgrafik) verwandeln, um diese Form als physikalisches Objekt zu nutzen. Nehmen wir an wir haben in den Assets ein wagen.png Die unter simplifyPoints() angegebene Zahl definiert die Menge der Vektorpunkte.
const pic = new Pic( "wagen.png" ).center();
const blob = new Blob( {
points: simplifyPoints( outlineImage( pic ), 10 ),
color: faint,
interactive: false
} ).loc( pic ).addPhysics();
pic.addTo( blob );
Achsen & join()
Beispiel 1 / Beispiel 2 / Beispiel 3 / Beispiel 4 / Beispiel5 / ZIM Docs Physics
Es gibt eine Methode join() mit der man 2 physikalische Objekte verbinden kann. Dieser Joint kann Drehjoint sein oder auch ein fester Joint. Das bestimmt man über den type. Man kann auch einen minimalen und maximalen Winkel festlegen, ähnlich einem Gelenk eines Lebewesens.
Die Parameter:
join(obj1, obj2, Point1, Point2, minWinkel, maxWinkel, type)
Ich habe mich anfangs sehr schwer getan, die Points zu definieren, da ich davon ausgegangen bin , das man diese irgendwie relativ zum Registierpunkt setzen muss und der liegt ja bekanntlich in der Mitte bei pyhsikalischen Objekten. Dieser Punkt kann auch da bleiben. Wenn ich jedoch ein Rad nicht in der Mitte eines Wagens anbringen möchts sondern irgendwo unten, damit die Karre fährt, dann muss man einfach den Wagen oder Karosserie mit mov() in eine Position bringen und das Rad auch und das zweite Rad natürlich auch. Man positioniert also alle Objekte so wie man sie verbinden möchte.
Dann gibt man mittels join() die beiden Objekte an, rad und wagen, setzt den ersten Point(rad.x, rad.y) den zweiten Point muss man gar nicht vergeben, wenn man einen festen Drehpunkt braucht, type ist "revolute"
physics.join( rad, wagen, new Point( rad.x, rad.y ), null, null, null, "revolute" );
physics.join( rad2, wagen, new Point( rad2.x, rad2.y ), null, null, null, "revolute" );
Im Beispiel 4 wurde die Gondel mit 2 join({type:"distance"})
mit dem Ballon verbunden. Damit das Ganze so funktioniert, mussten einigie Parameter aufeinander abgestimmt werden.
new Physics(2)
hat eine geringe Schwerkraft. Der ballon.addPhysics({angular:6})
hat eine starke Dämpfung der Drehung, sonst würde er sich bei seitlicher Bewegung stark drehen. Standardwert ist .5. Eine andere Möglichkeit, die sämtliche Drehung verhindert wäre
ballon.body.SetFixedRotation( true );
Der Ballon schwebt in einer vertikalen Position auf dem ersten Drittel der Bühnenhöhe. Das wurde erreicht mit:
Ticker.add(
() => { ballon.force( null, (H / 3 - ballon.y)*3 ); }
);
Beispiel 5
Hier wurde eine Pleuelstange an einem Rad befestigt. Damit die Stange auf einer Waagerechten bleibt, und ich keine Lösung gefunden habe, eine lineare Verbindung zu schaffen, habe ich einen Distance Joint gemacht, mit einer sehr großen Länge.
Beispiel 6
Hier habe ich noch einen Joint hinzugefügt, der mittels Minium und Maximum in der Drehung eingeschränkt ist.
maskBits() categoryBits()
Welche Objekte mit welchen Collidieren kann man mit dem addPhysics() Parameter maskBits und categoryBits einstellen.
siehe ZIM Beispiel / siehe Beispiel3
Im Beispiel werden alle 2 Sekunden mittels interval() Objekte erzeugt. Diese sollen nur mit dem Objekt blob kollidieren. Dazu werden folgende Parameter gesetzt addPhysics({maskBit: 2|2, categoryBits: 2}) Die erste 2 definiert, dass das Objekt nicht mit allen anderen Objekten collidiert, ansonsten wäre es 1. Die zweite 2 definiert, dass es nur mit den Objekten collidiert, die categoryBits: 2 haben. Dieses catengoryBits: 2 wurde auch dem blob, welcher der Wagen ist zugewiesen. So landen die Teile im Wagen aber nicht unten auf dem Boden.
obj.wiggle() | hin und her Animation
ZIM Doc / Beispiel 1, / Beispiel 2 / Beispiel 3
Mit der ZIM Methode wiggle()
kann man ein Objekt hin und her animieren lassen. Bei einer Animation landet man am Ende immer auf dem Startpunkt auch wenn man rewidn nutzt. Mit wiggle bewegt man sich vom Startpunkt aus in beide Richtungen. Außerdem kann man Zufallswerte setzen. Ich zeige anhand von 3 Beispielen wie man es macht und wie man es auch für physikalische Objekte einsetzen kann.
Die Parameter sind
wiggle(property, baseAmount, minAmount, maxAmount, minTime, maxTime, totalTime, type, ease, integer, id, startType, ticker, wait, pauseOnBlur, endOnStart)
type: "both" / "positive" / "negative" bestimmt die Richtung.
Mit den Animate Befehlen wie pauseAninate() , animate(), stopAnimate()
kann man das wiggeln pausieren und weiterlaufen lassen. Gibt man dem Objekt einen Bezeichner, kann man ihn darüber pausieren. siehe Animation
ball.pauseAnimate();
Beispiel
const physics = new Physics().drag();
const plat = new Pic( "TBalken.png" ).centerReg().addPhysics();
const rad = new Pic( "blueRad2.png" ).centerReg();
rad.addTo( plat );
rad.wiggle( "x", null, 200, 200, 2, 2 );
Physic wiggle
Beispiel 2
Wenn man ein physikalisches Objekt wigglen will muss man zuerst ein anders Objekt wiggeln, dann kann man in einer Timerschleife, dem physikalischem Objekt die Position und /oder andere Eigenschaften des gewiggelten Objekts zuweisen.
var balken = new Pic( "Tbalken.png" ).centerReg().addPhysics( 0 );
balken.wiggler = new Rectangle( 0, 0 )
.reg( CENTER )
.loc( balken )
.wiggle( "x", null, 400, 600, 6, 12 ) // null starts at current x
.wiggle( "y", null, 10, 50, .5, 2 )
.wiggle( "rotation", 0, 5, 10, 2, 10 );
const ticker = Ticker.add( () =>
{
balken.body
.loc( balken.wiggler.x, balken.wiggler.y )
.rot( balken.wiggler.rotation );
} );
In Beispiel 3 sieht man wie man das mit mehreren Objekten eines Tiles macht.
Zuerst wird das Tile mit den Bildern erzeugt.
Erste Schleife:
Dann wird jedem Tile Element ein Rechteck angehängt, welches mittels wiggle() eine Wackelbewegung bekommt.
Zweite Schleife:
In der nächsten Schleife werden alle Elemente des Tiles der Stage hinzugefügt mit addTo()
. Die Objekte bekommen addPhysics()
. Wenn man etwas mit einer Schleife aus einem Container oder Array entfernt, muss die Schleife rückwärts laufen. Deshalb der zweite Parameter reverse:true
tile.loop(function, true)
Dritte Schleife:
Nun sollen die Objekte, welche mittlerweile auf der Stage liegen, ständig die Position des Rechtecks (genannt wiggler) einnehmen. Die Objekte liegen aber nicht mehr im Tile balken, daher würde eine Schleife, wie sie zuvor definiert war, nicht funktionieren. Man kann aber über Tile.items
ein Array der ursprünglich zugewiesenen Objekte bekommen. items
ist eine readonly Eigenschaft der Tile Klasse. Jedes Element nimmt in der Zeitschleife die Position des wiggler Rechtecks ein. Das Rechteck kann man winzig klein oder transparent machen.
const balken = new Tile( new Pic("TBalken.png").centerReg(),6,3,600,180).centerReg().mov(0,50);
//Erste Schleife
balken.loop(item =>
{
item.wiggler = new Rectangle( 1, 1 )
.reg( CENTER )
.loc( item )
.wiggle( "x", null, 400, 600, 6, 12 ) // null starts at current x
.wiggle( "y", null, 10, 50, .5, 2 )
.wiggle( "rotation", 0, 5, 10, 2, 10 );
}, true);
//Zweite Schleife
balken.loop(
plat =>
{
plat.addTo().mov( rand( -100, 100 ), rand( 0, 300 ) ).addPhysics(false);
}, true );
//Dritte Schleife
const ticker = Ticker.add( () =>
{
loop( balken.items, plat =>
{
plat.body
.loc( plat.wiggler.x, plat.wiggler.y )
.rot( plat.wiggler.rotation );
} );
} );
Wiggle 5
Hier wurde ein Objekt auf einem physikalische Objekt positioniert und gewiggelt.
siehe Beispiel 5
Physic Obj auf Zim Obj positionieren / physics.attach()
Beispiel physic_attach.html
Man kann wohl kein physikalisches Objekt mit MotionControl bewegen, denn stattdessen nutzt man ja die control() Methode des pyhsikalischen Objekts, aber man kann ein anderes ZIM Objekt damit bewegen und dann ein physikalsiches Objekt auf der Position dieses Zim Objekts positionieren. Die Methode heißt attach() und wird dem Physics Objekt angehängt. Wenn man dem circle einen MotionController zuweisen würde, würde ball diesem circle folgen. siehe auch
const physics = new Physics();
const circle = new Circle(40,red).center();
const ball = new Circle(40, blue).centerReg().addPhysics();
physics.attach(circle, ball);
Sprite Physics Control
In diesem Beispiel geht es um Sprites welche mit einem Dynamo versehen werden und als Physikalische Objekte mittels Physics.control()
also Tasten gesteuert werden. Ich habe daszu einige Step by Step Beispiele erstellt.
SpriteMotion 1 / SpriteMotion 2 / SpriteMotion 3 / SpriteMotion4 / Sprite Scroller
SpriteMotion 1
Wir haben ein Physics
Objekt mit Boundary
und scroll:true
und ein Sprite mit addPhysics()
, welches den Bezeichern glump hat und die Methode control()
zugewiesen wurde, damit es per Tastatur gesteuert wird.
Ihm wurde ein Dynamo
zugewiesen um die einzlenen Frames des Sprites abzuspielen.
Mittels keydown
, wird der dynamo.percentSpeed
auf linken Taste keyCode 37 auf -200 gesetzt und auf rechte Taste auf keyCode 39 auf 200.
Wenn die Taste losgelassen wird keyup
ist dynamo.percentSpeed
auf 0 gesetzt.
So wirdemn die Frames desSprites gesteuert.
F.on( "keydown", e =>
{
if ( e.keyCode == 39 )
{
dynamo.percentSpeed = 200;
zog( e.keyCode );
}
if ( e.keyCode == 37 )
{
dynamo.percentSpeed = -200;
}
} );
F.on( "keyup", e =>
{
dynamo.percentSpeed = 0;
} )
Sprite Motion 2
In diesem Beispiel wird eine Jump Bewegung hinzugefügt. Doch vorerst wird bei control()
die vertikale Bewegung deaktiviert.
glump.control( { speed: 140, verical: false });
In der keydown
Funktion wird auch die Arrow Up Taste keyCode 38 abegefragt. Ein impulse(0,-200)
wird erzeugt und der Dynamo wird gestoppt, stattdessen wird mit der run()
Methode eine ander Sprite Abfolge abgespielt. Nach Ende dieser Abfolge wird der Dynamo wieder gestartet.
if ( e.keyCode == 38 )
{
glump.impulse( 0, -100 );
dynamo.pause( true );
glump.run(
{
label: "cry",
time: 1,
loop: false,
call: () =>
{
dynamo.pause( false )
}
}
);
}
Wenn die Taste losgelassen wird, wird dynamo.percentSpeed
nur dann auf 0 gesetzt, wenn die losgelassene Taste nicht die Arrow Up Taste war. Es kann ja sein, dass Arrow Up und links oder rechts gleichzeitig gedrückt wurden.
F.on( "keyup", e =>
{
//38 ist der up-Arrow
if ( e.keyCode != 38 )
{
dynamo.percentSpeed = 0;
}
} );
SpriteMotion3
In diesem Beispiel wollen wird verhindern, dass das Springen auch dann ausgelöst werden kann, während sich das Objekt in der Luft befindet. Das Thema wurde oben schon unter Jump behandelt.
Dem physikalischem Objekt glump wird eine boolsche Variable ground angehängt und auf true
gesetzt, wenn glump etwas berührt. Das wird ermittelt durch die contact()
Methode.
glump.contact( obj =>
{
glump.ground = true;
} );
In der keydown
Methode auf Druck der ArrowUp Taste wird diese Variable glump.ground auf true
abgefragt und dann auf false
gesetzt.
if ( e.keyCode == 38 && glump.ground )
{glump.ground = false;
//impulse etc.
}
Sprite Motion4
In diesem Beispiel wurden einige Plattformen hinzugefügt, und ein paar Modifikationen an der Spielfigur namens glump vorgenommen.
Die Begrenzung der Spielfigur war zu groß. Dadurch sah es so aus, als würde sie den Boden nicht berühren. Der Parameter contract
der Methode addPhysics({contract:20})
verkleinert die Begrenzung. Damit die Figur sich nicht dreht wurde folgendes gesetzt.
glump.body.SetFixedRotation( true );
Die Platformen sind ein Tile mit einer Reihe und einem Abstand von 350. Damit die einzelnen Elemente physikalische Objekte werden können, wurden sie mittels einem rückwärtigem Loop mit addTo() der Stage hinzugefügt, dann wurde ihnen zuerst eine veränderte vertikale Position und dann addPhyics(false) zugewiesen. false damit sie auf Position bleiben
i ist ein durchlaufender Index. Modula i%3 gibt den Rest einer Division durch 3 zurück., also 0,1,2,0,1,2, ... Dadurch bilden jeweils 3 Platformen eine Art Treppe.
Sprite Jump & Run Enemy
Beispiel5 / Beispiel6
Das folgende Beispiel gibt es in vielen Jump und Run Spielen. Auf einer Platform gibt es einen Feind der hin und her läuft und nicht berührt werden darf. Die Animation wurde nicht mit wiggle gemacht, da ich nicht weiß, wie man das mit dem Dynamo kombinieren könnte, der den Ablauf der Einzelbilder im Sprite steuert.
Stattdessen hab ich ein klitzekleines Rechteck als Platzhalter animiert und das physikalische Objekt in einem Timer an dessen Position gesetzt. Im Beispiel wurde außerdem der Sensor auf true gesetzt addPhysics({sensor:true}),
so dass der Sprite zwar auf contact()
des Balls reagiert aber nicht in physikalischer Weise.
Beispiel 7
Hier wurde die Platform inklusive Vampir in einer Klasse angelegt. Auch die Lampe ist nun eine Klasse, so dass man damit eine Bühne mit Hindernissen und Bonuspunkten erstellen kann.
Beispiel 8
In diesem Beispiel wurden die Klassen der Ziellampe class Lampe()
um eine Funktion bonus()
erweitert. Der Sprite in der Lampe hat außerdem eine Eigenschaft hitAmount mit dessen Wert wird der Punktestand erhöht, wenn sie vom ball getroffen wird. Wenn die Lampe blinkt also die Methode bonus()
aufgerufen wurde, ist dieser hitAmount erhöht. Dadurch erhält der Spieler mehr Punkte wenn die Lampe in dem Zustand getroffen wird. Nach 10 Sekunden wird dieses Blinken automatisch beendet. Das geschieht innerhalb der Klasse mit timeout()
. Der Aufruf der Methode bonus()
wird jeder Instanz mit einem Intervall zugewiesen. Dabei ist der Zeitwert des jeweiligen Intervals variabel druch einen zufälligen Wert von- bis.
interval({min:20, max:60},()=>{lampe1.bonus();});
Beispiel 9
Zwei Frames wurden erstellt, damit die Punkte Anzeige und Sound-On-Off Anzeige nicht scrollt. siehe ZIM Tips FRAME
Das Textfeld Instanzname score und die Punkte Instanzname punkte werden noch vor dem Erstellen des ersten Frames erzeugt, weil es Elemente sind, die in beiden Frames gebraucht werden.
Im zweiten Frame wird der Soundbutton inklusive onOff Funktion eingefügt. Er braucht keinen Zugriff auf Elemente im ersten Frame, denn der Sound wird einfach generell an und aus gestellt.
Das Textfeld score wird im zweiten Frame hinzugefügt und mittels Ticker zeigt es den Wert von punkte an.
ZIM Beispiel
In diesem Beispiel werden einige Scroller hinzugefügt. Ich weiß aber noch nicht wie man Hindernisse oder Platformen einfügen könnte.
RAF Request Animation Frame Problem
Die Animationen oder Geschwindigkeiten mit Box 2D Physic Engine können je nach PC oder Monitor variieren. Einige Monitore können eine Framerate von 120 Frames per Sekunde abspielen und das bedeutet, dass die requestAnimationFrame() Methode von Javascript nicht mit 60 fps sondern mit 120 fps läuft, also doppelt so schnell.
Wie man ein Spiel oder eine Animation auf eine Framerate von 60 Frames per Second erzwingt kann man im folgendem tollen Beitrag erfahren Standardize your Javascript
Alles schön und gut, hier in diesem Beispiel hab ich in einem Ticker die Framerate auf 60 fps beschränkt. Aber das löst noch nicht das framerate Problem, wenn man beispielsweise control()
oder impulse()
zuweist.
Es gibt die Möglichkeit mittels physics.timeStep
den Ticker der Physics Welt zu verändern. Der Standardwert 0.05 läuft bestens wenn requestAnimationFrame()
mit 60fps ausgeführt wird. Wir müssen diesen Wert verändern, und an eine andere Framerate oder anders ausgedrückt an eine andere Hertz Frequenz des Monitors anzupassen. Bei 120 Hz oder 120fps muss der Wert halbiert werden. Um das zu erreichen muss man zuerst ermitteln mit wieiviel fps oder Hertz wir es zu tun haben. Das kann man machen in dem man requestAnimationFrame()
eine Sekunde lang ausführt und bei jedem Aufruf eine Variable hochzählt. Nennen wir sie mal frame. Anhand der Anzahl von frames bestimmen wir den physics.timeStep
physics.timeStep = 0.05 / ( frames / 60 );
Man kann das Ganze auch auf eine halbe Sekunde reduzieren, dann wären es nur 30 frames.
physics.timeStep = 0.05 / ( frames / 30 );
Mit diesem Codeschnipsel sollte es funktionieren.
let physics = new Physics();
let loopCheck = true;
let frames = 0;
function checkFrameRate ()
{
if ( loopCheck )
{
window.requestAnimationFrame( checkFrameRate );
frames++
}
}
timeout( .5, () =>
{
loopCheck = false;
physics.timeStep = 0.05 / ( frames / 30 );
} );
checkFrameRate();
Nun kann man ein pyhsikalisches Objekt mit control() steuern.