Pong'da topun raketten zıpladığı zaman yönünü nasıl hesaplarsınız?


28

Oyun geliştirmedeki bu çok Hello World-y problemi kafamı sarmaya çalışıyorum. XNA'da bir TicTacToe oyunu hazırladım, sanırım bir sonraki adım Breakout klonu olurdu .

Oyun programlama hakkında hiçbir bilgim olmadığını ve hatta hangi matematiğe hangi matematiğe başvurmam gerektiğini aklımdan çıkarmayın. Bu yüzden bu soruyu soruyorum.


Soruya: Ekranın altındaki rakete çarptığında topun nereye sıçraması gerektiğini nasıl belirleyebilirim?

Bunun gibi bir şey olacağını hayal ediyorum:

  1. Gelen topun hızını ve açısını yakalayın.
  2. Çubuğa nerede dokunduğunu (çok sol, çok sağ, orta) ve dış alanlara dokunduğunda buna göre daha yüksek bir hız verdiğini tespit edin.
  3. Burası benim sıkışıp kaldığım yer. Hehe.

Herhangi bir görüş var mı? Bunun basit bir cevap tipi soru olmadığını anladım, ancak eminim ki herkes bir noktada karşı karşıya.

Bu web sitesinde önerilen Lineer Cebir kitabını okuyorum , ancak burada başvurup uygulamam konusunda hala hiçbir fikrim yok.


Ayrılmadan önce pong yazın, sonra top, duvar ve kürek sınıflarını dışa aktarabilir ve farklı tuğla ve poweruplarla çalışacak şekilde genişletebilirsiniz. Artı, pong'u kopuşmaktan daha basit düşünürdüm.
Gizli

Yanıtlar:


30

Ana sayfamda pong üzerinde kullandığım ilgili mantık : (lütfen okumaya başlamadan önce oynayın, böylece aşağıdaki kodla elde ettiğim etkiyi biliyorsunuz)

Temel olarak, top raketle çarpıştığında, yönü tamamen göz ardı edilir; raketin ortasından ne kadar çarpıştığına göre yeni bir yön verilir. Top, raketin tam ortasına vurursa, tamamen yatay olarak gönderilir; sağ kenara vurursa, aşırı bir açıyla (75 derece) uçar. Ve her zaman sabit bir hızda hareket eder.

var relativeIntersectY = (paddle1Y+(PADDLEHEIGHT/2)) - intersectY;

Raketin orta Y değerini alın ve topun Y kesişimini çıkarın. Eğer raket 10 piksel yüksekse, bu sayı -5 ile 5 arasında olacaktır. Buna "göreceli kesişme" diyorum, çünkü şimdi "raket alanı" içinde, topun raketin ortasına göre kesiştiği nokta.

var normalizedRelativeIntersectionY = (relativeIntersectY/(PADDLEHEIGHT/2));
var bounceAngle = normalizedRelativeIntersectionY * MAXBOUNCEANGLE;

Bağıl kesişimi alın ve raket yüksekliğinin yarısına bölün. Şimdi -5 ile 5 arasındaki sayımız -1 ile 1 arasında bir ondalıktır; o oluyor normalize . Ardından, topun sıçramasını istediğiniz maksimum açı ile çarpın. Onu 5 * Pi / 12 radyan (75 derece) olarak ayarladım.

ballVx = BALLSPEED*Math.cos(bounceAngle);
ballVy = BALLSPEED*-Math.sin(bounceAngle);

Son olarak, basit trigonometri kullanarak yeni top hızlarını hesaplayın.

Bu tam olarak istediğiniz etki olmayabilir veya normalize edilmiş göreceli kavşaklığı azami bir hız ile çarparak da bir hız belirlemek isteyebilirsiniz; bu, topun bir raketin kenarına vurması durumunda daha hızlı veya merkezin yakınında durması durumunda daha yavaş çalışmasını sağlar.


Muhtemelen bir vektörün neye benzeyeceği ya da topların sahip olduğu vektör değişkenini (hız ve yön) nasıl kaydedebileceğim üzerine bir kod istiyorum.

Bir vektör dolaylı olarak hem hız hem de yön içerir. Vektörümü "vx" ve "vy" olarak saklıyorum; yani, x yönündeki hız ve y yönündeki hızdır. Fizikte bir giriş dersi almadıysanız, bu size biraz yabancı gelebilir.

Bunu yapmamın nedeni, gerekli çerçeve başına hesaplamaları azaltmak; Her çerçeve, sadece yapmak x += vx * time;ve y += vy * time;zaman milisaniye cinsinden son karesinden bu yana zaman, nerede (dolayısıyla hızları milisaniye başına piksel içindedir).


Topu eğme yeteneğinin uygulanmasına ilişkin olarak:

Öncelikle, topun çarptığı anda raketin hızını bilmeniz gerekir; bu, raketin geçmişini takip etmeniz gerekeceği için, raketin geçmiş konumlarından birini veya daha fazlasını bilmeniz, böylece hareket edip etmediklerini görmek için mevcut konumuyla karşılaştırabilmeniz anlamına gelir. (pozisyondaki değişiklik / zamandaki değişiklik = hız; bu nedenle 2 veya daha fazla pozisyona ve bu pozisyonların zamanlarına ihtiyacınız var)

Şimdi , pratik olarak hareket ettiği eğriyi temsil eden, ancak topun gerçek dünyadaki dönüşüne eşdeğer olan topun açısal bir hızını izlemeniz gerekir . Sıçrama açısını kürek ile çarpışma sırasında topun göreceli konumundan nasıl enterpolasyon yapacağınıza benzer şekilde, bu açısal hızı (veya döndürme) çarpışmadaki raket hızından da enterpolasyon yapmanız gerekecektir. Sadece döndürme açısı ile yaptığınız gibi döndürmeyi ayarlamak yerine, topun mevcut döndürmesini eklemek veya çıkarmak isteyebilirsiniz, çünkü bu, oyunlarda iyi çalışma eğilimindedir (oyuncu topun döndüğünü görebilir ve dönmesine neden olabilir. daha da çılgınca, ya da düz hareket etmesini sağlamak için dönüşe karşı koyma).

Bununla birlikte, bunun en yaygın olan ve onu uygulamanın en kolay yolu olmasına rağmen, bir sıçramanın gerçek fiziğinin yalnızca vurduğu nesnenin hızına dayanmadığını unutmayın; açılı bir hıza sahip olmayan (dönüşsüz) bir nesnenin açılı bir yüzeye çarpan bir cisim, üzerine verilen bir dönüşe sahip olacaktır. Bu daha iyi bir oyun tamircisine yol açabilir, bu yüzden buna bakmak isteyebilirsiniz, ancak bunun arkasındaki fizikten emin değilim, bu yüzden açıklamaya çalışmayacağım.


Bu etki ne için gidiyorum; çubuğun kenarlarına çarptığından daha yüksek hız. Bunu yazmak için zaman ayırdığınız için teşekkürler. Yine de bazı şeyleri anlamakta güçlük çekiyorum; örneğin, ilk keskin nişancıda 'kesişme' nedir? Ayrıca, 'paddle1Y' çubuğun yüksekliği doğru mu?

kesişme Y, topun raketle kesiştiği konumdur. Şu anda bile anlamadığım dürüst bir hesaplama yapıyorum, ama esasen çarpıştığı zaman topun Y değeri. raket1Y, raketin Y değeridir, ekranın üstünden; PADDLEHEIGHT, raketin yüksekliğidir ("bar").
Ricket

"Eğri" toplarına izin vermek için ne eklemelisiniz? Örneğin, top rakete çarpmak üzereyken, top eğrisini oluşturmak için raketi hareket ettirirsiniz. Bunun gibi bir şey: en.wikipedia.org/wiki/Curve_ball
Zolomon 7:10

Düzenlemeye bakın ve ne düşündüğünüzü bana bildirin (bir şey hakkında daha fazla bilgiye ihtiyacınız olursa, bir şey alamadım, vb.)
Ricket

Teşekkürler! Mükemmel cevap, her zaman bu efekti elde etmek için nasıl yapıldığını merak ettim!
Zolomon

8

Bunu yaptığımdan bu yana bir süre geçti, ama sanırım bu hakka sahipim.

Mükemmel bir çarpışma göz önüne alındığında, yansıma açısı geliş açısına eşittir.

Raketinizin normalini biliyorsunuz (düz bir yüzey varsayarak): N Topunuzun orijinal pozisyonunu biliyorsunuz (zamanınızın başlangıcında): P Topun yeni pozisyonunu biliyorsunuz (zamanın sonunda): P 'Çarpışma noktanızı biliyorsunuz: C P -> P' segmentinin raketinizden geçtiğini hesapladığınızı varsayarsak, yeni yansıyan pozisyonunuz (P '') şöyle olacaktır:

P '+ 2 * (N * (P' nokta-N))

N * (P 'nokta -N) alt ifadesi, topun kat ettiği çarpışma normali boyunca derinliği hesaplar. Eksi işareti, normal yönün tersine derinliği kontrol ettiğimiz gerçeğini düzeltmektir.

P '+ 2 * alt ifadenin kısmı, topu çarpışma derinliğinin 2 katı kadar çarpışma düzleminin üzerine geri hareket ettirir.

Mükemmel çarpışmadan daha az bir çarpma istiyorsanız, 2 faktörünü (1 + (1-k)) olarak değiştirin, burada k sürtünme katsayınızdır. Kusursuz bir çarpışma, ak değerinin 0 olup, yansıma açısının tam olarak gelen açının açılmasına neden olur. 1 k değeri, topun çarpışma düzleminin yüzeyinde kalacağı bir çarpışmaya neden olur.

Yeni hız vektörünüz, V '', yönünüz P '' - C olacaktır. Normalize edin ve gelen hızınızla çarpın ve sonuçta ortaya çıkan hız büyüklüğünüz aynı olurdu, fakat yeni yönde. Elde edilen hızı artıracak (l> 1) veya azaltacak (l <1) katsayısı (l) ile çarparak bu hızda maymun oluşturabilirsiniz.

Özetlemek:

P '' = P '+ (1-k) * (N * (P nokta-N)) V' '= l * V * ((P' '- C) / | P' '- C |)

K ve l'nin sizin seçtiğiniz katsayılar olduğu yerler.


5

Yansıma ya "doğru" yapılabilir ya da "kolay" yapılabilir.

"Doğru" yol, duvarlara dik vektörleri hesaplamaktır. 2B'de bu oldukça kolay ve muhtemelen sadece onları zor kodlayabilirsiniz. Ardından, yansıtma aşaması esasen hareketin "paralel" bileşenini sağlam bırakır ve "dik" bileşeni tersine çevirir. Bunun için web'de muhtemelen detaylı bilgi var, belki MathWorld'de bile.

"Kolay" yol, bir duvara çarptığınızda X veya Y hareketini olumsuzlamaktır. Eğer yan duvarlara çarptıysanız, X'i ihmal edersiniz. Yukarıya vurursanız Y'yi de düşürürsünüz. Topu hızlandırmak istiyorsanız, sadece istediğiniz şeyi arttırın; X ve Y hızlarını çarparak mevcut yönünde hızlandırabilir veya yalnızca bir eksende hızlandırabilirsiniz.


Yukarıda tarif edilen "kolay" yol ve "doğru" yol aslında aynı değil midir?
Tom

Duvarlar ana eksenler boyunca bulunuyorsa, bunlar tamamen aynıdır. Duvarlar tüm X, Y ve Z eksenleri boyunca değilse, hayır, ikisi tamamen farklıdır.
dash-tom-bang

5

Ben de kendimde bir arkanoid-ish oyunu yapıyorum ve rakete çarptığında topun nasıl davranması gerektiğine dair çözümün günah / cos yaklaşımına girmekten çok daha basit ve daha hızlı olduğunu düşünüyorum. Böyle bir oyun. İşte yaptığım şey:

  • Tabii ki, top hızı zaman içinde arttığından, x öncesi / sonrasına enterpolasyon yapıyorum, y, doğru çarpışma algılamasını sürdürme adımlarını, her hız bileşenini oluşturulan vektörün modülüne bölerek hesaplanan tüm "stepX" ve "stepY" 'den ilmekledi mevcut ve gelecekteki top pozisyonları tarafından.

  • Eğer rakete karşı bir çarpışma olursa, Y hızını 20'ye bölerim. Bu "20", top raketin yanlarına çarptığında ortaya çıkan maksimum açıyı bulduğum en uygun değerdir, ancak bunu herhangi bir şekilde değiştirebilirsiniz ihtiyaçlarınız, sadece bazı değerlerle oynayın ve sizin için daha iyisini seçin. Bölünerek, bu sayıya göre ilk oyun hızım olan (20) 5 olan bir hız söyleyelim (0.25). Bu hesaplama, hız zaman içerisinde örneğin 15 olan maksimum hız değerime kadar yükseldiğinde açılarımı oldukça orantılı tutar (bu durumda: 15/20 = 0.75). Raketimin (x) y koordinatlarının orta kolda olduğu göz önüne alındığında (x ve y, raketin merkezini temsil eder), daha sonra bu sonucu, top pozisyonu ve raket pozisyonu arasındaki fark ile çarpıyorum. Fark ne kadar büyükse sonuçta ortaya çıkan açı. Bunun yanı sıra, orta koldaki bir kepçeyi kullanarak, topun merkezin hesaplanması konusunda endişelenmenize gerek kalmadan çarptığı tarafa bağlı olarak x artışı için doğru işareti alırsınız. Sahte kodda:

N = 0 ila modül ...

çarpışma belirlenirse, o zaman speedX = - (speedY / 20) * (paddleX - ballX); hızY = -sıcakY;
çıkış; eğer bitiyorsa

...

x = x + stepX; y = y + adımY;

sonu

Remeber, her zaman basit şeyler yapmaya çalış. Umut ediyorum bu yardım eder!


4

Breakout'taki raket, tarif ettiğiniz stili takip ettiğinde genellikle kavisli bir yüzey olarak modellenir. Geliş açısı, raketin üzerine vurduğu yere göre değişir. Ölü merkezde eğriye doğru teğet çizgi kesinlikle yataydır ve top beklendiği gibi yansır. Merkezden ayrıldıkça, eğriye olan teğet giderek açılı hale gelir ve top bunun sonucu olarak farklı şekilde yansır.

Kilit nokta, yansıtma açısının, topun hızı değil, değişen şey olmasıdır. Top hızı genellikle zamanla yavaşça artar.


1
Kavisli bir yüzey olarak söylüyorsunuz ve bu kafamda mantıklı geliyor ama bir kez kod açısından düşünmeye çalıştığımda işler bulanıklaşıyor. Bunu kodda bir değişken veya başka bir şey olarak nasıl ilan edebilirim.

Gibi bir şey angle = 1 - 2 * (ball.x - paddle.left) / paddle.widthsize 1 ile -1 arasında bir sayı verecektir; bu (oyun mekaniğiniz için ayarlanan değerlerin bir kısmı) topun çarpıştığı noktadaki teğet çizginin eğimidir. Kesin yatay olandan ziyade bu çizgiyi yansıtın.

4

Nolan Bushnell , geçtiğimiz hafta sonu SIEGE'de bir açılış konuşması yaptı ve orijinal pong ile benzer bir konu hakkında konuştu. Karmaşık hesaplamalar yapmak zorunda değilsin. Hte panelinin sol tarafına vurursanız, topu sola gönderin. Sağ taraf için de aynısını yapın.

Başlamak için sol ve sağ taraf için açıyı 45 derece yapabilirsiniz. Oyunu bitirdikten sonra geri dönüp bunu daha karmaşık hale getirmek isterseniz, bunu başlayabildiğiniz kadar basit hale getirebilirsiniz.


1
Açılış konuşmasının da, bunun matematiksel bir karar değil de bir tasarım kararı olduğu yönündeydi: “geliş açısı = yansıma açısı” doğru olurdu ama zayıf oyun için. Ek olarak, orijinal Pong ve Breakout'ta, hız kaç tane top / kürek çarpışmalarının bir fonksiyonuydu (zamanla hızlanır). Ayrıca, belirli sayıda isabetten sonra raket boyutunu da küçültürler. Topun dümdüz yukarı gitmesine izin vermekten kaçınırdım, o zaman kürek orada süresiz olarak bırakılabilir.
Ian Schreiber

4

Breakout, fizik tabanlı oyun programlama dünyasına dalmaya başlamak için klasik bir yeni başlayanlar. Temelde, top duvara çarptığında sıçrama hareketi vardır. Yukarıda söylendiği gibi, insidans açısı yansıma açısına eşittir. Ama topu rakete çarptığını düşündüğünüzde. Mantık 3 bölüme ayrılmıştır. 1.) Top, raketin orta kısmına çarpıyor. 2.) Top, raketin sol tarafına çarpıyor. 3.) Top, raketin sağ tarafına vurur.

Merkez kısmı dikkate aldığınızda: Topa çarptığında uygulananın sıçrama etkisinin farklı olmasına gerek yoktur. Top sadece normal olarak saptırılır. Fakat iki taraftan birine basıldığında durum farklıdır.

Topa sol tarafa çarptığında, yani topun ekranın sol tarafından gelen topunu düşünün ve raket ile sağ taraftan geliyorsunuz. Daha sonra topa sol tarafla vurduğunuzda, topun .ie.le.le geldiği yöne, aynı açıyla geldiği yöne yansıması gerekir. Aynı durum tam tersi de geçerlidir. Sağ kısımda da aynı şey geçerlidir.

Topun vurulduğunda sola veya sağa doğru bu hareketi daha inanılır kılar.

Umarım en azından akıllıca bir fikir edinmişsindir.


1

Raketin merkezi ile topun Y çarptığı ve aradığı nokta arasındaki mesafeyi hesapladığınızı hayal edin d. Diyelim ki dtop kürek merkezinin üstüne çıktığında pozitif bir değere sahip olabiliriz. Şimdi topunuzun d * -0.1Y hızını ekleyebilir ve yön değiştirmeye başlayacaktır. Javascript'te kolayca c # 'a çevrilebilecek bir örnek:

var canvas = document.querySelector('canvas');
var resize = function () {
  canvas.width = innerWidth;
  canvas.height = innerHeight;
};
resize();
var ctx = canvas.getContext('2d');
var ball = {
  size: 3,
  x: 1,
  y: canvas.height/2,
  vx: 2,
  vy: 0
};
var paddle = {
  height: 40,
  width: 3,
  x: canvas.width/2,
  y: canvas.height/2
};
addEventListener('mousemove', function (e) {
  paddle.y = e.clientY - (paddle.height/2);
});
var loop = function () {
  resize();
  ball.x += ball.vx;
  ball.y += ball.vy;
  if (ball.x > canvas.width || ball.x < 0) ball.vx *= -1; // horiz wall hit
  if (ball.y > canvas.height || ball.y < 0) ball.vy *= -1; // vert wall hit
  if (ball.x >= paddle.x && ball.x <= paddle.x + paddle.width && ball.y >= paddle.y && ball.y <= paddle.y + paddle.height) {
    // paddle hit
    var paddleCenter = paddle.y + (paddle.height/2);
    var d = paddleCenter - ball.y;
    ball.vy += d * -0.1; // here's the trick
    ball.vx *= -1;
  }
  ctx.fillRect(ball.x,ball.y,ball.size,ball.size);
  ctx.fillRect(paddle.x,paddle.y,paddle.width,paddle.height);
  requestAnimationFrame(loop);
};
loop();
body {overflow: hidden; margin: 0}
canvas {width: 100vw; height: 100vh}
<canvas></canvas>



0

Merhaba Son zamanlarda bir top oyunu yapmaya çalıştım ve bunun için bir çözüm buldum. Peki ne yaptım: Biz oyun oynarken raket hareket ediyor. Koordinat sistemim olduğu gibi kaldı, tuvalin sol üst noktası 0,0. Raket bu koordinat sisteminde hareket ediyor. X ekseni 0'dan tuval genişliğine ve y ekseni 0'dan tuval yüksekliğine işaret eder. 100 boyda ve 20 boyda sabit büyüklüğünde bir raket yarattım. Sonra etrafına hayali bir daire çiziyorum. Top rakete çarptığında raketin merkez noktasını hesaplarım

double paddleCenter=Squash.paddle.getXpos()+Squash.paddle.getPaddleWidth()/2;

Daha sonra, merkezin topun mevcut konumundan bu şekilde çıkmasını sağlarım, koordinat sistemi kürek merkezinde olur, ballCenter topun kürekle çarptığı noktadır (- (paddlewidth + r) .. 0 .. (paddlewidth + r )) bu, kürek üzerindeki vuruş noktasını yeniden ölçeklendirmekten başka bir şey değildir

double x0 = ballCenterX-paddleCenter;

Çemberin kesişme noktasını topun vuruş noktasının (x0) yardımıyla hesaplayın, bu bir yeniden hesaplamadır, biz zaten x0 koordinatına sahip olan dairede y koordinatını istiyoruz ve y ekseni için bir kapak gerekliydi

double y0 = -Math.sqrt(paddleRadius*paddleRadius-x0*x0);

kürek çevresinde tanımlanmış dairenin normal denkleminin türevini radis kürek ile hesaplayınRadius f (x, y) = x ^ 2 + y ^ 2-r ^ 2

double normalX=2*x0;
double normalY=2*y0;

normal yüzey için bir birim vektör elde etmek üzere N vektörünü normalize et

double normalizer=Math.sqrt(normalX*normalX + normalY*normalY);
normalX=normalX/normalizer;
normalY=normalY/normalizer;

Şimdi kürek için normalleştirilmiş (birim) yüzey normalleri var. Yeni yönü bu yüzey normları ile hesaplayın, bu, yansıma vektörü formülü yardımıyla hesaplanacaktır: new_direction = old_direction-2 * dot (N, old_direction) * N, ancak normal yüzey yukarı bakacak şekilde normalin topun rakete çarptığı noktadan noktaya değişiyor olmak

double eta=2; //this is the constant which gives now perfect reflection but with different normal vectors, for now this set to 2, to give perfect reflection
double dotprod=vX*normalX+vY*normalY;
vX=vX-eta*dotprod*normalX;//compute the reflection and get the new direction on the x direction
vY=-vY;//y direction is remain the same (but inverted), as we just want to have a change in the x direction

Bu sorunla ilgili çözümümü yayınladım. Daha fazla ayrıntı ve oyunun tamamı için github depomu görebilirsiniz:

https://github.com/zoli333/BricksGame

Java ile tutulması ile yazılmış. Ball.java'da bu konuda yapılan bir başka çözüm daha var, yeniden ölçeklendirme gerçekleşmiyor, koordinat koordinat sistemini kürek merkez noktasına hareket ettirmiyorum, bunun yerine yukarıdakilerin hepsini üst-üste 0,0 koordinat sisteminden göreceli olarak hesapladım. raketin orta noktası. Bu da işe yarıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.