![]() |
|
||||||||||
|
|||||||
|
|
« Предыдущая тема | Следующая тема » |
| Опции темы | Опции просмотра |
|
![]() |
![]() |
|
|||||
|
Регистрация: Oct 2006
Сообщений: 26
|
Столкнулся с очередной головоломкой!
Наглядный пример - пули должны разбиваться о блок, что и происходит, пока пули летят через фрейм или реже (первая часть примера). Но стоит запустить их в каждом фрейме (вторая часть) - как нате вам.. Видно под таким напором блок устоять не может и поддается ![]() Ширина пули больше её шага. Плюс ширина блока. Перескакивание невозможно! И если fps маленький - отлично видно, что пули ПЕРЕСЕКАЮТ блок. Основной код: var aBlt:Array = []; // массив для хранения пуль var j:Number = 0; var fMode:Boolean=true; mBlck.x = 500; stage.addEventListener(Event.ENTER_FRAME, fn1); stop(); function fn1(event:Event):void{ mBlck.x-=1; // двигаем блок навстречу пулям if(mBlck.x<50){ mBlck.x=500; fMode=!fMode; // переключаем режимы 'стрельбы' } if(fMode){ // режимы 'стрельбы' if(j++>0){ // добавляем пулю через фрейм j=0; var mc:MovieClip=new mBullet(); mc.x = 0; mc.y = 100 + Math.random() * 30; this.addChild(mc); aBlt.push(mc); } } else { // ..тоже самое! Но каждый фрейм. mc = new mBullet(); mc.x = 0; mc.y=300 - Math.random() * 30; this.addChild(mc); aBlt.push(mc); } // проверяем на пересечение с блоком for each(var m:MovieClip in aBlt){ if(mBlck.hitTestObject(m)){ m.fnHit(); // сообщаем пуле о попадании aBlt.splice(aBlt.indexOf(m), 1); // удаляем из списка this.removeChild(m); } } } function fnDel(zzz:DisplayObject):void { this.removeChild(zzz); zzz = null; } var m:MovieClip=MovieClip(this.parent); var nStp:Number=10; // шаг для пули this.addEventListener(Event.ENTER_FRAME, fn1); function fn1(event:Event):void{ if(x>550){ // если вылетели за этот предел.. this.removeEventListener(Event.ENTER_FRAME, fn1); // ..удаляем обработчик.. m.fnDel(this); // ..и себя return; } x+=nStp; // двигаемся на этот шаг } function fnHit():void{ this.removeEventListener(Event.ENTER_FRAME, fn1); } |
|
|||||
|
Предлагаю более подробно ознакомиться с методами "hitTestObject()" и "hitTestPoint()" в вашей ситуации я бы поступил иначе:
1) Проверка дистанции от объекта до пули. public function hitTest (target:DisplayObject):void { for (var i:int = 0; i < objects.length; i++) { if (target != objects[i]) { // Тут мы проверяем дистанцию. "Википедия, Вычисление дистанции". if (dis < needDis) { // Вуаля, столкновение. =) } } } } public function hitTest (target:DisplayObject):void { for (var i:int = 0; i < objects.length; i++) { if (target != objects[i]) { // Тут мы проыеряем на касание пули с объектом. if (target.x < objects[i].x + 10 && target.x > objects[i].x - 10 && target.y < objects[i].y + 10 && target.y > objects[i].y - 10 ) { // Вуаля, столкновение. =) } } } }
__________________
Спросишь, дурак на минуту. Если не спросишь, дурак на всю жизнь! Последний раз редактировалось stweet; 08.03.2011 в 01:19. |
|
|||||
|
Регистрация: Nov 2010
Сообщений: 497
|
Код выше - это плохая идея. Вообще, менять массив любым способом по ходу его итерации - плохая идея. Во втором случае в некоторые моменты времени с массивом пересекаются две соседних плашки. Удаляется только первая (вторая при итерации пропускается). На следующей итерации она удалится, но не удалится следующая и т.д. С учетом размера и скорости плашек, "пропускание" накапливается и в конце концов пуля преодолевает потенциальный барьер
. В первом случае такого не происходит, так как две пули с плашкой одновременно не пересекаются. Ну а hitTestObject здесь не при чем, он то как раз работает правильно.stweet, не поможет изменение проверки. |
|
|||||
|
Регистрация: Oct 2006
Сообщений: 26
|
stweet Проверка была математической. Без всяких hitTest. Грешил, что где-то накосячил в расчетах. Постепенно выкинул все что мог пытаясь найти причину. Плюнул и написал этот пример с нуля, чтобы исключить все нестыковки. Но не тут то было.. Пришел к мысли, что ошибка связана с массивом, но как её поймать так и не решил.
maxkar Вот пасибо! А то я так и не понял в чем секрет slice! Помогли исправить данную проблему таким советом: for each(var m:MovieClip in aBlt) -> for each(var m:MovieClip in aBlt.slice()). Получается во время обхода всех элементов массива - я выдергивал из него один элемент - массив сжимался - и на следующем обходе я "терял" один элемент? Но чем помогает slice? slice выполняется каждый раз в начале цикла? Последний раз редактировалось SiDDD; 08.03.2011 в 03:56. |
|
|||||
|
Регистрация: Nov 2010
Сообщений: 497
|
Цитата:
for (var i : int = 0; i < aBlt.length; i++) if (...) aBlt.splice(aBlt.indexOf(aBlt[i]), 1); Цитата:
var tmp : Array = aBlt.slice(); for (var i : int = 0; i < tmp.length; i++) if (checkIntersection(tmp[i])) aBlt.splice(aBlt.indexOf(tmp[i])); Цитата:
Хотя в вашем случае slice все таки тяжеловат. Лишняя копия, затем поиск в массиве (хотя и быстрый) и удаление. Можно проще, с явной итерацией по индексу: var idx : int = 0; while (idx < aBlt.length) { if (checkIntersection(aBlt[i]) aBlt.splice(i, 1); else idx++; } В вашем конкретном случае есть и еще один принципиально отличный от приведенных выше вариант. Он использует то, что пули отсортированы по горизонтали и если какая-то пуля еще не долетела до стенки, все остальные тоже не долетели. Тогда проверка идет только по одному элементу: while (aBlt.length > 0 && checkIntersection(aBlt[0])) { var m : MovieClip = aBlt.shift(); // удаляет первый элемент из aBlt m.fnHit(); this.removeChild(m); } var firstMiss : int = 0; while (firstMiss < aBlt.length && checkIntersection(aBlt[0])) firstMiss++; if (firstMiss > 0) for each (var m : MovieClip in aBlt.splice(0, firstMiss)) { m.fnHit(); this.removeChild(m); } Выбирать в данном случае стоит тот вариант, котороый вам понятнее ![]() |
|
|||||
|
Регистрация: Oct 2006
Сообщений: 26
|
Цитата:
На деле - исходная точка пули и блоки могут быть где угодно.В итоге имею два варианта: - первый (кто точно знает - 'aBlt.slice()' создает копию исходного массива при КАЖДОЙ итерации или только один раз при вхождении в цикл?) for each(var m:MovieClip in aBlt.slice()){ if(mBlck.hitTestObject(m)){ m.fnHit(); this.removeChild(m); aBlt.splice(aBlt.indexOf(m), 1); } } var m:MovieClip; var i:int=aBlt.length-1; while(i){ m=aBlt[i]; if(mBlck.hitTestObject(m)){ m.fnHit(); this.removeChild(m); aBlt.splice(aBlt.indexOf(m), 1); } i--; } ====================================================================================== Цитата:
..проверил таким способом: ... trace("start"); for each(var m:MovieClip in fnMy()){ if(mBlck.hitTestObject(m)){ m.fnHit(); this.removeChild(m); aBlt.splice(aBlt.indexOf(m), 1); } } trace("end"); ... function fnMy():Object{ trace("fn"); return aBlt.slice(); } Получается ф-ция вызывается только один раз при первом входе в цикл. ====================================================================================== Более быстрым оказался цикл 'while': Странное поведение 'for each' при 50% отсеивания ![]() (NNN=-1) Отсеивается 100%......while: 60.....for each: 95 (NNN=5000) Отсеивается 50%.....while: 32.....for each: 645!! (NNN=20000) Отсеивается 0%.....while: 3.......for each: 3 var aBlt:Vector.<Object> = new Vector.<Object>(); // массив для хранения пуль var obj:Object; var o:Object; var t:int; var i:int; mBlck.x=NNN; stage.addEventListener(Event.ENTER_FRAME, fn1); stop(); function fn1(event:Event):void{ for(i=0; i<10000; i++){ obj=new Object(); obj.x=i; aBlt.push(obj); } var t = getTimer(); i=aBlt.length-1; while(i){ o=aBlt[i]; if(o.x>mBlck.x){ fnZ(o); aBlt.splice(i, 1); } i--; } tOut.text="" + (getTimer()-t); /* var t = getTimer(); for each(o in aBlt.slice()){ if(o.x>mBlck.x){ fnZ(o); aBlt.splice(aBlt.indexOf(o), 1); } } tOut.text="" + (getTimer()-t); */ } function fnZ(tmp:Object):void { var zzz:Object=tmp; } Последний раз редактировалось SiDDD; 08.03.2011 в 18:27. |
|
|||||
|
Регистрация: Nov 2010
Сообщений: 497
|
В while с декрементом багу поправьте. Там первый элемент не проверяется. Условие должно быть while(i >= 0).
Да, по производительности while с конца на самом деле будет не хуже итерации по for each. При удалении элемента в "середине" массива этот массив нужно сдвигать. Если сдвигать "конец" массива, то при итерации "с конца" там будут только "выжившие" элементы. А при цикле for each придется сдвигать и элементы, которые потом будут удалены. Как раз это и дает сильный рост при удалении половины элементов на for each. Флеш-плеер может пытаться оптимизировать удаление (удаление на границе оптимизирует, это видно по вашим тестам), но в этом случае это сделает итерации for each и while эквивалентными по времени. Тест на отсеивание 50% не совсем честный по отношению к while. Там все удаляемые элементы в конце, поэтому никаких сдвигов не происходит. Элементы стоит сделать удаляемые хотя бы через один, в этом случае время будет более адекватно отражать общий случай. И еще замечание. Если у вас в реальном приложении будет много удалений в середине массива, быстреер всего делать фильтрацию, это дает максимум 1 проход по массиву + затраты на выделение памяти. Псевдокод: Ну и варианты того же с созданием копии только при необходимости (обнаружили удаляемый элемент). Но если случай простой (до сотен элементов, в середине удаляются единицы), то такая оптимизация скорее всего не нужна. |
|
|||||
|
Регистрация: Oct 2006
Сообщений: 26
|
Исправил чутка по своему:
|
![]() |
![]() |
Часовой пояс GMT +4, время: 12:04. |
|
|
« Предыдущая тема | Следующая тема » |
|
|