Di Pong, bagaimana Anda menghitung arah bola ketika memantul dari dayung?

28

Saya mencoba membungkus kepala saya di sekitar masalah Hello World-y ini dalam pengembangan game. Saya telah membuat game TicTacToe di XNA jadi saya kira langkah selanjutnya adalah klon Breakout .

Ingatlah bahwa saya tidak memiliki pengetahuan tentang pemrograman game atau bahkan matematika apa yang harus saya terapkan di mana. Itu sebabnya saya menanyakan pertanyaan ini.


Untuk pertanyaan: Bagaimana saya bisa menentukan di mana bola harus memantul ketika menyentuh dayung di bagian bawah layar?

Saya membayangkan itu akan menjadi seperti:

  1. Tangkap kecepatan dan sudut dari bola yang masuk.
  2. Mendeteksi di mana ia menyentuh bar (paling kiri, paling kanan, tengah) dan sesuai dengan itu memberikan kecepatan yang lebih tinggi jika menyentuh area luar.
  3. Di sinilah aku terjebak. Hehe.

Ada wawasan? Saya menyadari ini bukan jenis pertanyaan jawaban langsung, tapi saya yakin itu adalah pertanyaan yang semua orang hadapi.

Saya membaca buku Aljabar Linier yang direkomendasikan di situs web ini, tetapi saya masih tidak tahu apakah saya harus menerapkannya di sini.

Anko
sumber
Menulis pong sebelum breakout, Anda kemudian dapat mengekspor bola, dinding dan kelas dayung dan memperpanjang mereka sehingga mereka bekerja dengan berbagai jenis batu bata dan powerups. Plus, saya akan menganggap pong lebih sederhana daripada breakout.
Incognito

Jawaban:

30

Inilah logika yang relevan yang saya gunakan pada pong di beranda saya : (silakan mainkan sebelum membaca, agar Anda tahu efek yang saya capai dengan kode berikut)

Pada dasarnya, ketika bola bertabrakan dengan dayung, arahnya sama sekali diabaikan; itu diberikan arah baru sesuai dengan seberapa jauh dari pusat dayung itu bertabrakan. Jika bola mengenai dayung tepat di tengah, itu dikirim persis horisontal; jika menyentuh tepat di tepi, ia terbang pada sudut yang ekstrem (75 derajat). Dan selalu bergerak dengan kecepatan konstan.

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

Ambil nilai tengah Y dari dayung, dan kurangi persimpangan Y dari bola. Jika dayung tingginya 10 piksel, angka ini akan berada di antara -5 dan 5. Saya menyebutnya "intersect relatif" karena berada di "ruang dayung" sekarang, persimpangan bola relatif ke tengah dayung.

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

Ambil persimpangan relatif dan membaginya dengan setengah tinggi dayung. Sekarang angka -5 ke 5 kita adalah desimal dari -1 hingga 1; itu dinormalisasi . Kemudian gandakan dengan sudut maksimum yang Anda inginkan agar bola memantul. Saya mengaturnya ke 5 * Pi / 12 radian (75 derajat).

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

Akhirnya, hitung kecepatan bola baru, menggunakan trigonometri sederhana.

Ini mungkin bukan efek yang Anda inginkan, atau Anda mungkin ingin juga menentukan kecepatan dengan mengalikan persimpangan relatif yang dinormalisasi dengan kecepatan maks; ini akan membuat bola melaju lebih cepat jika menyentuh dekat tepi dayung, atau lebih lambat jika mengenai dekat pusat.


Saya mungkin ingin beberapa kode pada apa yang akan terlihat seperti vektor atau bagaimana saya bisa menyimpan variabel dari vektor bola memiliki (kecepatan dan arah).

Vektor berisi kecepatan dan arah, secara implisit. Saya menyimpan vektor saya sebagai "vx" dan "vy"; yaitu, kecepatan dalam arah x dan kecepatan dalam arah y. Jika Anda belum mengambil kursus pengantar dalam fisika, ini mungkin agak asing bagi Anda.

Alasan saya melakukan ini adalah karena mengurangi perhitungan per-bingkai yang diperlukan; setiap frame, Anda cukup lakukan x += vx * time;dan di y += vy * time;mana waktu adalah waktu sejak frame terakhir, dalam milidetik (oleh karena itu kecepatannya dalam piksel per milidetik).


Mengenai menerapkan kemampuan untuk melengkung bola:

Pertama-tama, Anda harus mengetahui kecepatan pemukul pada saat bola mengenai; yang berarti Anda harus melacak sejarah pemukul, sehingga Anda dapat mengetahui satu atau lebih dari posisi masa lalu pemukul sehingga Anda dapat membandingkannya dengan posisi saat ini untuk melihat apakah itu bergerak. (perubahan posisi / perubahan waktu = kecepatan; jadi Anda perlu 2 posisi atau lebih, dan waktu posisi itu)

Anda sekarang juga perlu melacak kecepatan sudut bola, yang secara praktis mewakili kurva sepanjang perjalanannya, tetapi setara dengan putaran bola dunia nyata. Mirip dengan bagaimana Anda akan menginterpolasi sudut pantulan dari posisi relatif bola pada tabrakan dengan dayung, Anda juga perlu menginterpolasi kecepatan sudut ini (atau memutar) dari kecepatan dayung pada tabrakan. Daripada hanya mengatur putaran seperti yang Anda lakukan dengan sudut pantulan, Anda mungkin ingin menambah atau mengurangi putaran bola yang ada, karena itu cenderung bekerja dengan baik dalam permainan (pemain dapat melihat bola berputar, dan menyebabkannya berputar bahkan lebih liar, atau melawan putaran dalam upaya membuatnya lurus).

Namun, perhatikan bahwa meskipun ini adalah akal sehat yang paling umum dan mungkin cara termudah untuk mengimplementasikannya, fisika sebenarnya dari pantulan tidak hanya bergantung pada kecepatan objek yang ditabraknya; sebuah objek tanpa kecepatan sudut (tanpa putaran) yang mengenai permukaan pada suatu sudut, akan memiliki putaran yang diberikan padanya. Ini mungkin mengarah ke mekanik permainan yang lebih baik, jadi Anda mungkin ingin melihat ini, tapi saya tidak yakin dengan fisika di baliknya sehingga saya tidak akan mencoba menjelaskannya.

Ricket
sumber
Efek itulah yang saya inginkan; semakin cepat kecepatan saat menyentuh tepi bar. Terima kasih banyak telah meluangkan waktu untuk menulis ini. Saya mengalami beberapa kesulitan memahami beberapa hal; misalnya, pada snipper pertama, apa itu 'intersectY'? Juga, 'paddle1Y' adalah ketinggian bilah yang benar?
intersectY adalah posisi bola di mana ia memotong dayung. Saya melakukan perhitungan yang terlalu rumit yang sejujurnya saya bahkan tidak mengerti sekarang, tetapi pada dasarnya itu adalah nilai Y dari bola pada saat itu bertabrakan. paddle1Y adalah nilai Y dari dayung, dari bagian atas layar; PADDLEHEIGHT adalah ketinggian dayung ("bar").
Ricket
Apa yang harus Anda tambahkan untuk memungkinkan bola "melengkung"? Misalnya, ketika bola akan mengenai dayung, Anda memindahkan dayung untuk membuat bola melengkung. Sesuatu seperti ini: en.wikipedia.org/wiki/Curve_ball
Zolomon
Lihat hasil edit, dan beri tahu saya apa pendapat Anda (jika Anda butuh info lebih lanjut tentang sesuatu, jangan dapatkan sesuatu, dll.)
Ricket
Terima kasih! Jawaban luar biasa, saya selalu bertanya-tanya bagaimana cara mencapai efek itu!
Zolomon
8

Sudah lama sejak saya melakukan ini, tapi saya pikir saya sudah benar.

Diberikan tabrakan sempurna, sudut pantulan sama dengan sudut kejadian.

Anda tahu normal dayung Anda (menganggap permukaan datar): N Anda tahu posisi awal bola Anda (di awal cap waktu Anda): P Anda tahu posisi bola yang baru (di akhir cap waktu): P 'Anda tahu titik tumbukan Anda: C Dengan asumsi Anda menghitung bahwa segmen P -> P' melewati dayung Anda, posisi pantulan baru Anda (P '') adalah:

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

Sub-ekspresi N * (P 'dot -N) menghitung kedalaman sepanjang normal tabrakan yang dilalui bola. Tanda minusnya adalah mengoreksi fakta bahwa kita sedang memeriksa kedalaman yang berlawanan dengan arah normal.

P '+ 2 * bagian dari subekspresi menggerakkan bola kembali ke atas bidang tabrakan dengan 2 kali kedalaman tabrakan.

Jika Anda menginginkan tabrakan yang kurang sempurna, ubah faktor 2 menjadi (1 + (1-k)) di mana k adalah koefisien gesekan Anda. Tabrakan sempurna memiliki nilai ak 0, yang menyebabkan sudut pantulan persis sama dengan sudut yang masuk. Nilai k 1 menyebabkan tabrakan di mana bola akan tetap berada di permukaan bidang tabrakan.

Vektor kecepatan baru Anda, V '', arah akan menjadi P '' - C. Normalisasikan dan kalikan dengan kecepatan masuk Anda dan besarnya kecepatan yang dihasilkan akan sama, tetapi di arah baru. Anda dapat menggunakan kecepatan itu dengan mengalikannya dengan koefisien, l, yang akan meningkatkan (l> 1) atau mengurangi (l <1) kecepatan yang dihasilkan.

Untuk meringkas:

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

Di mana k dan l adalah koefisien yang Anda pilih.

nan0meter
sumber
5

Refleksi dapat dilakukan "benar" atau dilakukan "mudah."

Cara "benar" adalah dengan menghitung vektor yang tegak lurus terhadap dinding. Dalam 2D ​​itu cukup mudah dan Anda mungkin bisa hanya kode keras mereka. Kemudian, langkah refleksi pada dasarnya meninggalkan komponen "paralel" gerakan utuh dan membalikkan komponen "tegak lurus". Mungkin ada informasi terperinci di web untuk ini, bahkan mungkin di MathWorld.

Cara "mudah" adalah dengan meniadakan gerakan X atau Y saat Anda menabrak dinding. Jika Anda menabrak dinding samping, Anda akan meniadakan X. Jika Anda mencapai bagian atas Anda meniadakan Y. Jika Anda ingin mempercepat bola, cukup tambahkan apa pun yang Anda inginkan; Anda dapat mempercepatnya ke arah saat ini dengan mengalikan kecepatan X dan Y atau Anda dapat mempercepat hanya pada satu sumbu.

dash-tom-bang
sumber
Bukankah cara "mudah" dan cara "benar" yang dijelaskan di atas pada dasarnya sama ??
Tom
Mereka persis sama jika dinding di sepanjang sumbu utama. Jika dinding tidak sepanjang sumbu X, Y, dan Z maka tidak, keduanya sama sekali berbeda.
dash-tom-bang
5

Saya juga membuat permainan arkanoid-ish dan saya pikir solusi tentang bagaimana bola seharusnya berperilaku ketika memukul dayung adalah lebih sederhana dan lebih cepat daripada masuk ke pendekatan sin / cos ... itu berfungsi dengan baik untuk keperluan suatu game seperti ini. Inilah yang saya lakukan:

  • Tentu saja, karena kecepatan bola meningkat dalam waktu saya interpolasi langkah sebelum / sesudah x, y untuk menjaga deteksi tabrakan yang akurat, melewati semua "stepX" dan "stepY" yang dihitung dengan membagi setiap komponen kecepatan dengan modulus vektor yang dibentuk oleh posisi bola saat ini dan masa depan.

  • Jika tabrakan dengan dayung terjadi, saya membagi kecepatan Y dengan 20. "20" ini adalah nilai paling nyaman yang saya temukan untuk mendapatkan hasil maksimum sudut saya ketika bola mengenai sisi dayung, tetapi Anda dapat mengubahnya ke apa pun kebutuhan Anda, hanya bermain dengan beberapa nilai dan pilih yang lebih baik untuk Anda. Dengan membagi, katakanlah kecepatan 5, yang merupakan kecepatan permainan awal saya dengan nomor ini (20), saya mendapatkan "faktor pantulan" sebesar 0,25. Perhitungan ini menjaga sudut saya cukup proporsional ketika kecepatan meningkat dalam waktu hingga nilai kecepatan maksimum saya yang, misalnya, bisa 15 (dalam kasus itu: 15/20 = 0,75). Mempertimbangkan bahwa paddle x, y coords berada di tengah-tengah (x dan y mewakili pusat dayung), saya kemudian melipatgandakan hasil ini dengan perbedaan antara posisi bola dan posisi dayung. Semakin besar perbedaannya, theearear the angle yang dihasilkan. Selain itu, menggunakan padlle midhandled, Anda mendapatkan tanda yang benar untuk kenaikan x tergantung pada sisi bola memukul tanpa harus khawatir menghitung tengah. Dalam pseudo-code:

Untuk n = 0 ke modulus ...

jika collision_detected maka speedX = - (speedY / 20) * (paddleX - ballX); speedY = -kecepatanY;
keluar; berakhir jika

...

x = x + stepX; y = y + stepY;

berakhir untuk

Ingat, selalu berusaha untuk menjaga hal-hal SEDERHANA. Saya harap ini membantu!

co_Opernicus
sumber
4

Dayung di Breakout, ketika mengikuti gaya yang Anda gambarkan biasanya dimodelkan sebagai permukaan melengkung. Sudut insidensi berubah berdasarkan di mana dayung itu mengenai. Di tengah mati garis singgung ke kurva benar-benar horisontal, dan bola mencerminkan seperti yang diharapkan. Saat Anda bergerak dari tengah, garis singgung ke kurva menjadi semakin miring, dan bola mencerminkan secara berbeda sebagai hasilnya.

Poin kuncinya adalah sudut pantulan, bukan kecepatan bola, yang berubah. Kecepatan bola umumnya hanya meningkat perlahan seiring waktu.


sumber
1
Anda mengatakan sebagai permukaan melengkung dan itu terdengar logis di kepala saya, tetapi begitu saya mencoba memikirkannya dalam hal kode, hal-hal menjadi kabur dengan cepat. Bagaimana saya bisa menyatakan bahwa dalam kode sebagai variabel atau sesuatu.
Sesuatu seperti angle = 1 - 2 * (ball.x - paddle.left) / paddle.widthakan memberi Anda angka antara 1 dan -1; ini (kali beberapa nilai tweak untuk mekanik game Anda) adalah kemiringan garis singgung pada titik bola bertabrakan. Merefleksikan garis itu daripada garis yang benar-benar horizontal.
4

Nolan Bushnell memberikan keynote di SIEGE akhir pekan lalu dan berbicara tentang masalah serupa dengan pong asli. Anda tidak perlu melakukan banyak perhitungan rumit. Jika Anda menekan ke arah bagian kiri panel hte, kirim bola ke kiri. Lakukan hal yang sama untuk sisi kanan.

Untuk memulai dengan Anda dapat membuat sudut untuk sisi kiri dan kanan 45 derajat. Setelah Anda menyelesaikan permainan Anda bisa jika Anda ingin kembali dan membuat ini lebih rumit tetapi membuat ini sesederhana mungkin untuk memulai.

lathomas64
sumber
1
Saya melihat keynote itu juga, maksudnya adalah bahwa ini adalah keputusan desain dan bukan keputusan matematika: "sudut datang = sudut refleksi" akan benar tetapi menghasilkan gameplay yang lemah. Selain itu di Pong dan Breakout asli, kecepatan adalah fungsi dari berapa banyak tabrakan bola / dayung yang ada (sehingga mempercepat seiring waktu). Mereka juga mengurangi ukuran dayung setelah sejumlah hit juga. Saya akan menghindari membiarkan bola lurus ke atas, maka Anda bisa meninggalkan dayung di sana tanpa batas.
Ian Schreiber
4

Breakout adalah karya pemula klasik untuk mulai terjun ke dunia pemrograman game berbasis fisika. Pada dasarnya bola memiliki gerakan memantul ketika mengenai dinding. Seperti seseorang di atas menyarankan sudut kejadian sama dengan sudut refleksi. Tetapi ketika Anda menganggap bola memukul dayung. Logikanya dibagi menjadi 3 bagian. 1.) Bola mengenai bagian tengah dayung. 2.) Bola mengenai bagian kiri dayung. 3.) Bola memukul posisi kanan dayung.

Ketika Anda mempertimbangkan bagian tengah: Anda tidak perlu membedakan efek pantulan dari apa yang diterapkan saat memukul bola. Bola hanya bisa dibelokkan secara normal. Tapi, ketika salah satu arah terkena, kasusnya berbeda.

Ketika bola dipukul di sisi kiri, yaitu pertimbangkan bola yang datang dari sisi kiri layar dan Anda datang dengan dayung dari sisi kanan. Kemudian ketika Anda memukul bola dengan bagian kiri, bola harus mencerminkan ke arah mana asalnya. Ya.Lalu ke depan dengan sudut yang sama dari mana asalnya. Sama halnya sebaliknya. Di bagian kanan juga hal yang sama berlaku.

Gerakan bola ke arah kiri atau kanan saat dipukul membuatnya lebih bisa dipercaya.

Semoga Anda mendapatkan idenya, setidaknya secara logika bijaksana. Terima kasih

Wisnu
sumber
1

Bayangkan Anda menghitung jarak antara pusat dayung dan titik di mana bola Y mengenai dan menyebutnya d. Misalkan dmemiliki nilai positif ketika bola membentur bagian atas tengah dayung. Anda sekarang dapat menambah d * -0.1kecepatan Y bola Anda dan itu akan mulai mengubah arah. Berikut adalah contoh dalam javascript yang dapat dengan mudah diterjemahkan ke dalam c #!

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>

rafaelcastrocouto
sumber
0

Hai, saya baru-baru ini mencoba membuat permainan bola dan menemukan solusi untuk ini. Jadi apa yang saya lakukan: Dayung itu bergerak saat kami memainkan permainan. Sistem koordinat saya dibiarkan apa adanya, titik kiri atas kanvas adalah 0,0. Dayung bergerak dalam sistem koordinat ini. Sumbu x menunjuk dari 0 ke lebar kanvas, dan sumbu y menunjuk pada 0 ke tinggi kanvas. Saya membuat dayung dengan ukuran fix 100 lebar dan 20 tinggi. Dan kemudian saya menggambar lingkaran imajiner di sekitarnya. Saat bola mengenai dayung, saya menghitung Centerpoint paddle

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

Lalu saya kurangi pusat dari posisi bola saat ini dengan cara ini sistem koordinat akan berada di tengah dayung, ballCenter adalah titik di mana bola mengenai paddle (- (paddlewidth + r) .. 0 .. (paddlewidth + r )) ini tidak lain hanyalah menyelamatkan titik memukul pada dayung

double x0 = ballCenterX-paddleCenter;

menghitung titik persimpangan lingkaran dengan bantuan titik memukul bola (x0) ini adalah perhitungan ulang, kami meminta koordinat y pada lingkaran dengan koordinat x0 yang sudah dikenal dan diperlukan flip untuk sumbu y

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

hitung turunan dari persamaan normal lingkaran yang didefinisikan di sekitar dayung dengan radis paddleRadius f (x, y) = x ^ 2 + y ^ 2-r ^ 2

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

menormalkan vektor N, untuk mendapatkan vektor satuan untuk permukaan normal

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

sekarang kita memiliki normals permukaan (satuan) yang dinormalisasi untuk dayung. Hitung arah baru dengan normals permukaan ini, ini akan dihitung dengan bantuan rumus vektor refleksi: new_direction = old_direction-2 * dot (N, old_direction) * N, tetapi alih-alih dengan permukaan normal selalu menunjuk ke atas, kehendak normal berubah dari titik ke titik di mana bola menyentuh dayung

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

Saya telah menerbitkan solusi saya untuk masalah ini. Untuk detail lebih lanjut dan untuk permainan lengkapnya Anda bisa melihat gudang github saya:

https://github.com/zoli333/BricksGame

ditulis dalam java dengan gerhana. Ada solusi lain untuk ini yang dikomentari di Ball.java, di mana pencabutan ulang tidak terjadi, saya tidak memindahkan sistem koordinat koordinat ke titik tengah paddle, alih-alih saya menghitung semua hal di atas dari topleft 0,0 sistem koordinat relatif terhadap titik tengah dayung. Ini juga berfungsi.

zoli333
sumber