Latest Blog
Multimedia Interaktif Tata Surya
Membuat Multimedia Pembelajaran Interaktif tentang tata surya
ALL BLOG
;
Kecerdasan buatan - simulasi lalu lintas dengan Flash AS3
Pendahuluan
Dalam suatu game yang kompleks dimana melibatkan gerakan otomatis dari sebuah mobil NPC (Non Playable Character), membuat sistem pathfinding untuk gerakan tersebut merupakan tantangan yang rumit. Dalam game Sim Taxi dan Ace Gangster misalnya, terdapat beberapa kendaraan berupa mobil yang bergerak mengelilingi peta kota secara otomatis dengan sistem top-down view. Untuk kedua kasus tersebut (Sim Taxi dan Ace Gangster) membuat gerakan mobil tidak terlalu rumit karena peta kota dibuat dengan sistem Array mapping, sehingga deteksi jalan, tikungan, pertigaan dan perempatan jalan cukup ditentukan dengan nilai array dimana objek tersebut berada.
Namun demikian, lain halnya jika akan membuat kota yang tidak memiliki array mapping (art base), tentunya mendeteksi kondisi jalan merupakan tantangan tersendiri. Untuk itu melalui tutorial ini, saya memberikan satu alternatif solusi yang pernah saya pakai dalam membuat simulasi lalu lintas pada peta berbasis gambar (art base).
Proses Pembuatan Simulasi lalu lintas Art Base dengan AS3
- Pada file aplikasi Flash, buatlah sebuah file baru AS3
- Dengan menggunakan drawing tool, buatlah sebuah gambar peta kota pandangan atas (warna merah adalah area yang tidak dapat dilewati), kemudian convert menjadi MovieClip "road_hit". Pastikan registration point movieclip tersebut di pojok kiri atas (Lihat gambar)
- Buatlah layer baru, ubah namanya menjadi layer start(Lihat gambar). Kemudian buatlah sebuah movieclip panah (menghadap ke atas). Selanjutnya atur posisinya di atas movieclip road_hit. Hal ini dimaksudkan sebagai posisi awal keluarnya mobil. Selanjutnya seleksi semua panah dan convert menjadi movieclip "car_start". Edit movieclip tersebut untuk memastikan agar registration point movieclip car_start sama dengan posisi registration point movieclip road_hit
- Selanjutnya buatlah gambar mobil dengan pandangan atas, lalu convert menjadi movieclip car_ai. Edit movieclip tersebut, tambahkan sebuah layar dan buatlah sebuah movieclip berupa lingkaran kecil yang akan kita gunakan sebagai sensor. Duplikasi movieclip sensor tersebut sebanyak 4 kali, letakkan diposisi depan (1 buah), dan samping masing-masing 2 buah. (lihat gambar)
- Tambahkan instance name pada masing-masing sensor tersebut, yaitu sensor_f, sensor_r, sensor_r2, sensor_l dan sensor_l2
- Setelah itu anda dapat menset nilai alpha dari sensor-sensor tersebut menjadi 0% sehingga tidak terlihat di layar.
- Hapus movieclip car_ai dari layar. Karena penambahan mobil ke layar nantinya menggunakan kode
- Agar dapat diimport oleh kode, tambahkan linkage pada movieclip car_ai
- Setelah asset siap, buatlah layer baru (layer kode), klik frame 1 layer kode. Buka panel Action(F9), dan tuliskan kode berikut
import flash.events.Event; import flash.events.MouseEvent; import flash.display.MovieClip; var game_active:Boolean = true; var ai_car_handler:MovieClip = new MovieClip(); function on_mouseup(e:MouseEvent){ game_active = !game_active; } stage.addEventListener(MouseEvent.MOUSE_UP, on_mouseup); function find_point(ob:Object):Object { var dist:Number = Math.sqrt(ob.x * ob.x + ob.y * ob.y); var rad:Number = Math.atan(ob.y / ob.x); var rot:Number = 0; if (ob.x > 0) { rot = rad * 180 / Math.PI; } else { rot = rad * 180 / Math.PI + 180; } return {a:dist, b:rot}; } function setup_ai_car(px:Number, py:Number, rot:Number, id:Number):void { var ob = new car_ai(); ob.name = "ai_" + id; ob.c_id = id; //ob.c_id_txt.text = String(id); ob.x = px; ob.y = py; ob.start_x = ob.x; ob.start_y = ob.y; ob.start_rot = rot; ob.rotation = rot; ob.d_f = find_point(ob.sensor_f).a; ob.r_f = find_point(ob.sensor_f).b; ob.d_l = find_point(ob.sensor_l).a; ob.r_l = find_point(ob.sensor_l).b; ob.d_l2 = find_point(ob.sensor_l2).a; ob.r_l2 = find_point(ob.sensor_l2).b; ob.d_r = find_point(ob.sensor_r).a; ob.r_r = find_point(ob.sensor_r).b; ob.d_r2 = find_point(ob.sensor_r2).a; ob.r_r2 = find_point(ob.sensor_r2).b; ob.dist = 0; ob.normal_speed = 2; ob.t_rot = rot; ob.ai_type = 1; ob.turn = 0; ob.wait = 0; ob.old_speed = 0; ob.old_ai = 1; ob.addEventListener(Event.ENTER_FRAME, move_ai_car); ai_car_handler.addChild(ob); } function move_ai_car(e:Event) { if (game_active){ var ob = e.currentTarget; //lurus if (ob.ai_type == 1) { ob.speed = ob.normal_speed; //find empty area find_road(ob, road_hit); if (ob.turn != 0) { ob.ai_type = ob.turn + 1; //left if (ob.turn == 1) { ob.t_rot = ob.rotation - 90; } if (ob.turn == 2) { ob.t_rot = ob.rotation + 90; } } } if (ob.ai_type == 2) { //left ob.speed = ob.normal_speed / 1.5; ob.rotation -= 4; if (c_rot(ob.rotation, ob.t_rot)<=2){ ob.rotation = ob.t_rot; ob.ai_type = 1; } } if (ob.ai_type == 3) { //right ob.speed = ob.normal_speed / 1.5; ob.rotation += 4; if (c_rot(ob.rotation, ob.t_rot)<=2){ ob.rotation = ob.t_rot; ob.ai_type = 1; } } if (ob.ai_type == 4) { //lurus ob.dist+=ob.speed; // > road width if (ob.dist>20){ ob.ai_type = 1; } } if (ob.ai_type == 5) { //lost ob.x = ob.start_x; ob.y = ob.start_y; ob.rotation = ob.start_rot; ob.ai_type = 1; } //ai_hit if (hit_other_car(ob) && ob.ai_type<5){ ob.old_ai = ob.ai_type; ob.ai_type = 6; trace(ob.c_id + "wait"); ob.old_speed = ob.speed; ob.wait = 30; } if (ob.ai_type == 6) { ob.speed = 0; ob.wait--; if (ob.wait<=0){ ob.speed = ob.old_speed; ob.wait = 0; trace(ob.c_id + "go"); ob.ai_type = ob.old_ai; } } ob.x += ob.speed * Math.sin(ob.rotation * Math.PI / 180); ob.y -= ob.speed * Math.cos(ob.rotation * Math.PI / 180); } } function c_rot(ob1:Number,ob2:Number):Number { var dis_angle:Number = Math.abs(ob1 - ob2) % 360; if (dis_angle > 180) { dis_angle = 360 - dis_angle; } return dis_angle; } function find_road(ob, hit) { var px_l:Number = ob.x + ob.d_l * Math.cos((ob.r_l + ob.rotation) * Math.PI / 180); var py_l:Number = ob.y + ob.d_l * Math.sin((ob.r_l + ob.rotation) * Math.PI / 180); var px_l2:Number = ob.x + ob.d_l2 * Math.cos((ob.r_l2 + ob.rotation) * Math.PI / 180); var py_l2:Number = ob.y + ob.d_l2 * Math.sin((ob.r_l2 + ob.rotation) * Math.PI / 180); var px_r:Number = ob.x + ob.d_r * Math.cos((ob.r_r + ob.rotation) * Math.PI / 180); var py_r:Number = ob.y + ob.d_r * Math.sin((ob.r_r + ob.rotation) * Math.PI / 180); var px_r2:Number = ob.x + ob.d_r2 * Math.cos((ob.r_r2 + ob.rotation) * Math.PI / 180); var py_r2:Number = ob.y + ob.d_r2 * Math.sin((ob.r_r2 + ob.rotation) * Math.PI / 180); var px_f:Number = ob.x + ob.d_f * Math.cos((ob.r_f + ob.rotation) * Math.PI / 180); var py_f:Number = ob.y + ob.d_f * Math.sin((ob.r_f + ob.rotation) * Math.PI / 180); var px_f2:Number = ob.x + 1.5*ob.d_f * Math.cos((ob.r_f + ob.rotation) * Math.PI / 180); var py_f2:Number = ob.y + 1.5*ob.d_f * Math.sin((ob.r_f + ob.rotation) * Math.PI / 180); ob.turn = 0; //left if (!hit.hitTestPoint(px_l,py_l,true) && hit.hitTestPoint(px_l2,py_l2,true)) { //lurus atau belok? if (!hit.hitTestPoint(px_f2,py_f2,true) && Math.random()*10<7){ ob.turn = 3; ob.dist = 0; }else{ ob.turn = 1; } } //right if (!hit.hitTestPoint(px_r,py_r,true) && hit.hitTestPoint(px_r2,py_r2,true)) { //lurus atau belok? if (!hit.hitTestPoint(px_f2,py_f2,true) && Math.random()*10<7){ ob.turn = 3; ob.dist = 0; }else{ ob.turn = 2; } } //front left if (hit.hitTestPoint(px_f,py_f,true)) { if (! hit.hitTestPoint(px_l,py_l,true) && ! hit.hitTestPoint(px_l2,py_l2,true)) { ob.turn = 1; }else if (! hit.hitTestPoint(px_r,py_r,true) && ! hit.hitTestPoint(px_r2,py_r2,true)) { ob.turn = 2; } } //out of area if (hit.hitTestPoint(px_l,py_l,true) && hit.hitTestPoint(px_l2,py_l2,true) && hit.hitTestPoint(px_r,py_r,true) && hit.hitTestPoint(px_r2,py_r2,true)){ ob.ai_type = 5; } } function hit_other_car(ob:Object):Boolean{ var px_f:Number = ob.x + 0.5*ob.d_f * Math.cos((ob.r_f + ob.rotation) * Math.PI / 180); var py_f:Number = ob.y + 0.5*ob.d_f * Math.sin((ob.r_f + ob.rotation) * Math.PI / 180); if (ai_car_handler.hitTestPoint(px_f,py_f,true)){ return true; }else{ return false; } } function add_ai_cars():void{ car_ai_start.visible = false; for (var i=0;i<12; i++){ var ob = car_ai_start.getChildAt(i); setup_ai_car(ob.x,ob.y,ob.rotation,i); } } addChild(ai_car_handler); add_ai_cars();
Jalankan aplikasi dengan menekan tombol Ctrl+Enter, maka hasil dari tutorial ini adalah sebagai berikut:
Penjelasan Kode
Penjelasan kode diatas adalah sebagai berikut :
- Untuk mendeteksi kondisi jalan, Movieclip car_ai menggunakan ke 5 sensornya. Dengan kode sederhana hitTestPoint, dapat ditentukan apakah sensor menyentuh jalan atau tidak, sehingga dapat ditentukan apakah sedang berada di jalan lurus, belokan, pertigaan maupun perempatan.
Dengan logika sederhana dibuat peraturan sebagai berikut :- Mobil bergerak menggunakan jalur kanan (US / Eropa)
- Mobil memiliki kecenderungan belok ke arah kanan
- Jika ada belokan ke kanan, maka mobil akan mengecek kondisi jalan apakah belokan atau pertigaan
- Jika belokan maka mobil akan belok
- Jika pertigaan atau perempatan, maka akan diacak untuk menentukan mobil belok atau lurus
- Fungsi ini digunakan untuk menggerakkan car_ai. Variabel ob.ai_type digunakan untuk menentukan jenis gerakannya, apakah lurus, berhenti atau belok.
- Sensor depan dari masing-masing mobil digunakan untuk menentukan deteksi tumbukan dengan mobil lain. Dengan menggunakan kode hitTestPoint, akan menghasilkan nilai true jika titik sensor depan menyentuh mobil lain, dan gerakan mobil akan dihentikan sementara sampai mobil yang berada di depannya lewat.
function find_road(ob, hit) { ... //left if (!hit.hitTestPoint(px_l,py_l,true) && hit.hitTestPoint(px_l2,py_l2,true)) { ... } //right if (!hit.hitTestPoint(px_r,py_r,true) && hit.hitTestPoint(px_r2,py_r2,true)) { ... } //front left if (hit.hitTestPoint(px_f,py_f,true)) { ... }
function move_ai_car(e:Event) { ...
function hit_other_car(ob:Object):Boolean{ var px_f:Number = ob.x + 0.5*ob.d_f * Math.cos((ob.r_f + ob.rotation) * Math.PI / 180); var py_f:Number = ob.y + 0.5*ob.d_f * Math.sin((ob.r_f + ob.rotation) * Math.PI / 180); if (ai_car_handler.hitTestPoint(px_f,py_f,true)){ return true; }else{ return false; } }
Kesimpulan
Sistem deteksi untuk simulasi lalulintas top down bisa dibuat dengan beberapa cara seperti dengan tile system (array mapping), maupun dengan menggunakan metode art base (dengan menggunakan sensor). Dengan cara tersebut kita dapat membuat kecerdasan buatan berupa simulasi gerakan mobil yang bergerak di dalam kota secara terus menerus dengan sedikit peluang untuk crash / macet.
File Sumber
File sumber Simulasi Mobil (CS5)
Share ( Ayo Berbagi )
Leave me a comment
untuk pertanyaan lebih baik di email langsung ke wandah [at] wandah [dot] com agar cepat direspon