2016-09-24 6 views
0

Ich versuche, ein Partikelexplosionssystem, wo jedes Partikel direkt von einem zentralen Punkt explodiert. Aber ich bekomme ein ziemlich funky Verhalten.Probleme bei der Berechnung der Richtung für die Partikelexplosion

Ich verwende Math.atan2(y2 - y1, x2 - x1) * 180/Math.PI;, um die Richtung zum Mittelpunkt zu berechnen, und dann die Richtung um 180 Grad vom Winkel subtrahieren.

Hier ist der Rest meines Codes mit den Teilchen zu tun:

for (var i = 0; i < le.length; i ++) { 
    // Create new particle element and append it to document. 
    lObj[i] = new Particle(0, 0, 9, 0, 0, document.createElement('span')); 
    lObj[i].spanEl.innerHTML = le[i]; 
    lObj[i].spanEl.style.position = 'relative'; 
    p.appendChild(lObj[i].spanEl); 
    // Find location of this particle. 
    loc[i] = lObj[i].spanEl.getBoundingClientRect(); 
    // Calculate direction toward center point and reverse away from it. 
    lObj[i].direction = directionToPoint(loc[i].left, loc[i].top, centX, centY) - 180; 
} 

var x = 0, 
    y = 0, 
    vel, 
    dir; 

function loop() { 
    for (var i = 0; i < le.length; i ++) { 
     // Update location of each particle 
     x = lObj[i].relX; 
     y = lObj[i].relY; 
     vel = lObj[i].velocity; 
     dir = lObj[i].direction; 
     dir = lObj[i].direction; 
     x += vel * Math.cos(dir * Math.PI/180); 
     y += vel * Math.sin(dir * Math.PI/180); 
     vel = (vel > 0) * (vel - 0.2); 
     lObj[i].relX = x; 
     lObj[i].relY = y; 
     lObj[i].velocity = vel; 
     lObj[i].spanEl.style.left = x + 'px'; 
     lObj[i].spanEl.style.top = y + 'px'; 
    } 
} 

Image of what's happening

Ernsthaft kann nicht dieses herauszufinden. Hilfe von euch Jungs würde sehr geschätzt werden!

Edit: Rest des Codes



    var p = document.getElementsByTagName('p')[0], 
     le = p.innerHTML.split(''); 

    p.innerHTML = ''; 

    var lObj = []; 

    function Particle (relX, relY, velocity, direction, keyframe, spanEl) { 
     this.relX = relX; 
     this.relY = relY; 
     this.velocity = velocity; 
     this.direction = direction; 
     this.friction = 0.1; 
     this.keyframe = keyframe; 
     this.spanEl = spanEl; 
    } 

    var loc = []; 

    var centX = 800, centY = 250; 

    var marker = document.getElementsByClassName('marker')[0]; 

    marker.style.left = centX + 'px'; 
    marker.style.top = centY + 'px'; 
+0

Anstatt mit einem Skalar (1d) Wert für die Geschwindigkeit in Kombination mit einer Richtung Überschrift verwendet werden, ist es viel einfacher, wenn Sie einen 2D-Vektor beibehalten . Wenn das erledigt ist, ist es egal, was der Winkel zum Ursprung ist - das spart ein bisschen Trigonometrie. Das heißt - warum versuchen Sie, 180 ° hinzuzufügen, um die Richtung umzukehren? Punktursprung = Vektor vom Ursprung zum Punkt. während Ursprungspunkt = Vektor von Punkt zu Ursprung. Die Verwendung von 2D-Vektoren macht grundlegende Teilchensysteme trivial. – enhzflep

+0

bitte auch den Code für 'Particle' und Beispieldaten für' le' und 'loc' angeben. – trincot

+0

Hey, danke! Der Rest des Codes wurde hinzugefügt. Könnten Sie versuchen, das, was Sie gerade gesagt haben, etwas anders zu formulieren, um es mir leichter zu machen, es zu verstehen? –

Antwort

0

Weiteres auf einen Kommentar oben, hier ist ein schlecht umgesetzt Partikelsystem unter Verwendung von 2D-Vektoren. Ich habe zusammen eine 'Animation' gehackt, mit einer For-Schleife - offensichtlich sollte ich window.requestAnimationFrame (https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) verwenden, aber das ist außerhalb des Rahmens dieses Beispiels.

Um die Position und Richtung jedes Partikels zu initialisieren, mache ich folgendes.

  1. erhalten zwei Zufallszahlen zwischen 0 und 1
  2. subtrahieren 0,5 von jedem, so habe ich zwei Zahlen im Bereich [-0,5 .. 0,5]
  3. Verwenden Sie die in 1 als Anfangs gewonnen Nummer Position.
  4. Normalisieren des Vektors vom Ursprung zu dieser Position ( durch die Position als ein Vektor sehen)
  5. Multipliziere sie mit einer Zahl zwischen 0 und 10. Die Partikel werden Zufallsrichtungen haben und eine Geschwindigkeit aufweisen, die aus Bereichen [0..10]
  6. erhalten Sie eine Zufallszahl [0..25] und fügen Sie 25 hinzu - das wird das Maximum sein Alter.

function byId(id){return document.getElementById(id)} 
 
window.addEventListener('load', onDocLoaded, false); 
 

 
var partArray = [], maxParticles=500; 
 

 

 
function onDocLoaded(evt) 
 
{ 
 
\t for (var i=0; i<maxParticles; i++) 
 
\t { 
 
\t \t var pos = new vec2_t(Math.random()-0.5, Math.random()-0.5); \t \t // give them a poition of [-0.5..0.5] 
 
\t \t var vel = pos.normalize(); 
 
\t \t vel.scalarMult(Math.random() * 10); \t \t \t \t \t \t \t \t // give them a velocity of [0..10] 
 
\t \t var maxAge = (Math.random() * 25) + 25; \t \t \t \t \t \t \t \t // age is in range [25..50] 
 
\t \t var newParticle = new part_t(pos, vel, maxAge); \t \t \t \t \t \t // create the particle 
 
\t \t partArray.push(newParticle); \t \t \t \t \t \t \t \t \t \t // and put it in our array 
 
\t } 
 
\t 
 
\t for (var y=0; y<5; y++) 
 
\t { 
 
\t \t drawParticles(); 
 
\t \t moveParticles(); 
 
\t } 
 
} 
 

 
function vec2_t(x,y) 
 
{ 
 
\t this.x = x; 
 
\t this.y = y; 
 
\t 
 
\t this.normalize = function() 
 
\t { 
 
\t \t var result = new vec2_t(0,0); 
 
\t \t var lenSq = (this.x*this.x) + (this.y*this.y); 
 
\t \t var len = Math.sqrt(lenSq); 
 
\t \t result.x = this.x/len; 
 
\t \t result.y = this.y/len; 
 
\t \t return result; 
 
\t } 
 
\t this.scalarMult = function(scalar) 
 
\t { 
 
\t \t this.x *= scalar; 
 
\t \t this.y *= scalar; 
 
\t } 
 
\t 
 
\t return this; 
 
} 
 

 
function part_t(position, velocity, maxAge) 
 
{ 
 
\t this.position = position; 
 
\t this.velocity = velocity; 
 
\t this.maxAge = maxAge; 
 
\t this.age = 0; 
 
\t return this; 
 
} 
 

 
function setPixel(x,y,ctx) 
 
{ 
 
\t var imgData = ctx.getImageData(x,y,1,1); 
 
\t imgData.data[ 0 ] = 255; 
 
\t imgData.data[ 1 ] = 0; 
 
\t imgData.data[ 2 ] = 0; 
 
\t imgData.data[ 3 ] = 255; 
 
\t ctx.putImageData(imgData,x,y); 
 
// \t console.log(x+','+y); 
 
} 
 

 
function drawParticles() 
 
{ 
 
\t var can = byId('partSysCanvas'); 
 
\t var ctx = can.getContext('2d'); 
 
\t var partNum; 
 
\t for (partNum=0; partNum<maxParticles; partNum++) 
 
\t { 
 
     // add 256,256 since this is canvas.width/2,canvas.height/2 
 
\t \t setPixel(256+partArray[partNum].position.x, 256+partArray[partNum].position.y, ctx); 
 
\t } 
 
} 
 
function moveParticles() 
 
{ 
 
\t for (var i=0; i<maxParticles; i++) 
 
\t { 
 
\t \t if (partArray[i].age < partArray[i].maxAge) 
 
\t \t { 
 
\t \t \t partArray[i].age++; 
 
\t \t \t partArray[i].position.x += partArray[i].velocity.x; 
 
\t \t \t partArray[i].position.y += partArray[i].velocity.y; 
 
\t \t } 
 
\t } 
 
}
<canvas width=512 height=512 id='partSysCanvas'></canvas>

+0

Danke :) Ich versuche eigentlich nicht nur ein explodierendes Partikelsystem, sondern explodierenden Text zu erstellen. Der Effekt, den ich will, ist, dass alle von der Mitte aus explodieren und nicht in alle zufälligen Richtungen. –

0

, dass einige Spaß. Auch hier habe ich auf Trigonometrie verzichtet, anstatt Vektoren zu verwenden. Die Buchstaben explodieren nicht von der Mitte - Text wird gezeichnet und dann explodieren die Buchstaben von diesem Punkt weg vom Explosionsort. Ich habe eine einheitliche Explosionsgeschwindigkeit für alle Partikel mit vel.scalarMult(10); festgelegt, die Sie stattdessen mit der darüber liegenden Zeile randomisieren können.

Ich habe mich auch nicht darum gekümmert, die Partikelgeschwindigkeit basierend auf ihrer Nähe zur Bombe zu skalieren. Alles explodiert einfach vom Sitz, wo wir wissen, dass die von der Explosion gefühlt Kraft abnimmt, wenn die Entfernung zunimmt.

Hier ist eine funktionierende Demo zum Spielen.

Ich sollte wirklich requestAnimationFrame

"use strict"; 
 
function newEl(tag){return document.createElement(tag)} 
 
function byId(id){return document.getElementById(id)} 
 
// useful for HtmlCollection, NodeList, String types (array-like objects without the forEach method) 
 
function forEach(array, callback, scope){for (var i=0,n=array.length; i<n; i++)callback.call(scope, array[i], i, array);} // passes back stuff we need 
 

 
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 
 
function vec2_t(x,y) 
 
{ 
 
\t this.x=x; 
 
\t this.y=y; 
 
\t this.equals = function(vec2){this.x = vec2.x; this.y = vec2.y;} 
 
\t this.addVec = function(vec2){this.x += vec2.x; this.y += vec2.y;} 
 
\t this.scalarMult = function(scalar){this.x *= scalar; this.y *= scalar;} 
 
\t this.vecLen = function(){return Math.sqrt(this.x*this.x + this.y*this.y);} 
 
\t this.normalize = function(){ let k = 1.0/this.vecLen(); this.scalarMult(k); } 
 
\t this.vecSub = function(vec2){this.x-=vec2.x;this.y-=vec2.y;} 
 
\t this.toString = function(){return"<"+this.x+","+this.y+">"} 
 
\t return this; 
 
} 
 

 
function part_t(vec2pos, vec2vel, domElem) 
 
{ 
 
\t this.pos = vec2pos; 
 
\t this.vel = vec2vel; 
 
\t this.domElem = domElem; 
 
\t return this; 
 
} 
 

 

 

 
var particleArray, timerId; 
 
let explosionOrigin = new vec2_t(156,110); 
 

 
window.addEventListener('load', onDocLoaded, false); 
 

 

 

 
function onDocLoaded(evt) 
 
{ 
 
\t particleArray = createPartSys('textInput', 'tgtContainer'); 
 
\t byId('stepBtn').addEventListener('click', onStepBtnClick); 
 
\t byId('resetBtn').addEventListener('click', onResetBtnClick); 
 
\t byId('animateBtn').addEventListener('click', onAnimateBtnClick); 
 
\t 
 
\t byId('pauseBtn').addEventListener('click', onPauseBtnClick); 
 
\t 
 
\t byId('tgtContainer').addEventListener('click', onClick); 
 
} 
 

 
function onStepBtnClick(evt) 
 
{ 
 
\t updatePartSys(particleArray); 
 
} 
 

 
function onAnimateBtnClick(evt) 
 
{ 
 
\t timerId = setInterval(function(){updatePartSys(particleArray);}, 100); 
 
\t this.setAttribute('disabled', 'true'); 
 
\t byId('pauseBtn').removeAttribute('disabled'); 
 
} 
 

 
function onPauseBtnClick(evt) 
 
{ 
 
\t clearInterval(timerId); 
 
\t this.setAttribute('disabled', 'true'); 
 
\t byId('animateBtn').removeAttribute('disabled'); 
 
} 
 

 
function onResetBtnClick(evt) 
 
{ 
 
\t var bombImg = byId('bomb'); 
 
\t byId('tgtContainer').innerHTML = ''; 
 
\t byId('tgtContainer').appendChild(bombImg); 
 
\t 
 
\t particleArray = createPartSys('textInput', 'tgtContainer'); 
 
\t 
 
\t byId('animateBtn').removeAttribute('disabled'); 
 
\t clearInterval(timerId); 
 
} 
 

 
function createPartSys(srcElemId, tgtElemId) 
 
{ 
 
\t let \t elem = byId(srcElemId); 
 
\t var str = elem.value, len=str.length; 
 

 
\t let result = []; 
 
\t let parent = elem; 
 
\t let curX = elem.offsetLeft - parent.offsetLeft; 
 
\t let curY = elem.offsetTop - parent.offsetTop; 
 

 
\t let bombImg = byId('bomb'); 
 
\t bombImg.style = 'position: absolute'; 
 
\t bombImg.style.left = (explosionOrigin.x - (bombImg.clientWidth/2))+'px'; 
 
\t bombImg.style.top = (explosionOrigin.y - (bombImg.clientHeight/2))+'px'; 
 
\t byId(tgtElemId).appendChild(bombImg); 
 
\t \t 
 
\t curY = 50; 
 
\t curX = 50; 
 
\t forEach(str, 
 
\t \t \t function(letter) 
 
\t \t \t { 
 
\t \t \t \t var span = newEl('span'); 
 
\t \t \t \t span.className = 'particle'; 
 
\t \t \t \t if (letter == ' ') letter = '&nbsp;' 
 
\t \t \t \t let h1 = newEl('h1'); 
 
\t \t \t \t h1.innerHTML = letter; 
 
\t \t \t \t span.appendChild(h1); 
 
\t \t \t \t span.style.left = curX + 'px'; 
 
\t \t \t \t span.style.top = curY + 'px'; 
 
\t \t \t \t byId(tgtElemId).appendChild(span); 
 
\t \t \t \t 
 
\t \t \t \t var pos = new vec2_t(curX,curY); 
 
\t \t \t \t 
 
\t \t \t \t curX += span.offsetWidth; 
 

 
\t \t \t \t var vel = new vec2_t(0,0); 
 
\t \t \t \t 
 
\t \t \t \t let letterOrigin = getCenter(span); 
 
\t \t \t \t 
 
\t \t \t \t vel.equals(letterOrigin); 
 
\t \t \t \t vel.vecSub(explosionOrigin); 
 
\t \t \t \t vel.normalize(); 
 
\t \t \t // \t vel.scalarMult((Math.random()*1) + 4); 
 
\t \t \t \t vel.scalarMult(10); 
 
\t \t \t \t 
 
\t \t \t \t var newPart = new part_t(pos,vel,span); 
 
\t \t \t \t result.push(newPart); 
 
\t \t \t } 
 
\t \t); 
 
\t return result; 
 
} 
 

 
function updatePartSys(partSys) 
 
{ 
 
\t forEach(
 
\t \t \t partSys, 
 
\t \t \t function(part, index, array) 
 
\t \t \t { 
 
\t \t \t \t part.pos.addVec(part.vel); \t \t \t \t \t \t // position += velocity 
 
\t \t \t \t var gravity = new vec2_t(0,0.98/3); \t \t \t \t // arbitrary value chosen 
 
\t \t \t \t part.vel.scalarMult(0.95); \t \t \t \t \t \t // velocity *= 0.95 \t - needs to be quite high. it simulates wind resistance 
 
\t \t \t \t part.vel.addVec(gravity); \t \t \t \t \t \t // velocity += gravity 
 
\t \t \t \t part.domElem.style.left = part.pos.x + "px"; 
 
\t \t \t \t part.domElem.style.top = part.pos.y + "px"; 
 
\t \t \t } 
 
\t \t); 
 
} 
 

 
function onClick(evt) 
 
{ 
 
\t let elemRect = byId('tgtContainer').getBoundingClientRect(); 
 
\t let posX = evt.clientX - elemRect.left, posY = evt.clientY-elemRect.top; 
 
\t explosionOrigin.x = posX; 
 
\t explosionOrigin.y = posY; 
 
\t onResetBtnClick(); 
 
} 
 

 
function getCenter(elem) 
 
{ 
 
\t let x = elem.offsetLeft + (elem.offsetWidth/2); 
 
\t let y = elem.offsetTop + (elem.offsetHeight/2); 
 
\t let result = new vec2_t(x,y); 
 
\t return result; 
 
}
#tgtContainer 
 
{ 
 
\t position: relative; 
 
\t height: 256px; 
 
\t width: 512px; 
 
\t border: solid 1px black; 
 
\t overflow: hidden; 
 
\t background-color: white; 
 
} 
 
.particle 
 
{ 
 
\t display: inline-block; 
 
\t position: absolute; 
 
} 
 
.panel 
 
{ 
 
\t display: inline-block; 
 
\t border: solid 1px #113; 
 
\t border-radius: 8px; 
 
\t margin: 8px; 
 
\t padding: 8px; 
 
\t background-image: url(https://www.gravatar.com/avatar/97c2d181ef6bbb9eee0c4033561c3891?s=48&d=identicon&r=PG); 
 
\t background-size: 100% 100%; 
 
} 
 

 
#textContainer 
 
{ 
 
\t display: block; 
 
} 
 

 
#textContainer textarea 
 
{ 
 
\t width: 100%; 
 
\t padding: 0; 
 
\t margin: 1px 0px; 
 
}
<div class='panel'> 
 
\t \t <div id='textContainer'><textarea id='textInput'>click to set bomb position</textarea></div> 
 
\t \t <hr> 
 
\t \t <button id='resetBtn'>Reset</button><button id='stepBtn'>Single Step</button> | <button id='animateBtn'>Animate</button><button id='pauseBtn' disabled>Pause</button> 
 
\t \t <hr> 
 
\t \t <div id='tgtContainer'> 
 
\t \t </div> 
 
\t </div> 
 
\t <svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" id='bomb'> 
 
\t \t <g transform="translate(0,-1020.3622)"> 
 
\t \t \t <path d="m23.23,15.84a10.55,10.55,0,1,1,-21.11,0,10.55,10.55,0,1,1,21.11,0z" transform="matrix(1.1875635,0,0,1.1875635,0.68612298,1020.367)" fill="#26201e"/> 
 
\t \t \t <path d="m23.23,15.84a10.55,10.55,0,1,1,-21.11,0,10.55,10.55,0,1,1,21.11,0z" transform="matrix(0.86603158,0,0,0.86603158,2.4299747,1024.1874)" fill="#333"/> 
 
\t \t \t <path d="m-13.04,19.32a1.964,1.964,0,1,1,-3.929,0,1.964,1.964,0,1,1,3.929,0z" transform="matrix(1.924285,1.1058108,-1.1908732,2.0723069,62.314757,1012.6494)" fill="#CCC"/> 
 
\t \t \t <path d="m15.69,1026c0.02518-5.037,7.647-7.396,8.907-2.969,0.7936,2.761,1.349,5.666,4.877,6.786" stroke="#888" stroke-width="1.5px" fill="none"/> 
 
\t \t \t <rect height="2.399" width="4.798" y="1026" x="13.31" stroke-width="0" fill="#26201e"/> 
 
\t \t \t <path fill="#F00" transform="translate(2.0203051,1022.13)" d="M29.8,10.53,27.1,9.62,24.82,11.32,24.86,8.477,22.54,6.833,25.25,5.989,26.1,3.271,27.74,5.595,30.59,5.558,28.89,7.839z"/> 
 
\t \t </g> 
 
\t </svg>

Verwandte Themen