Форум Flasher.ru
Ближайшие курсы в Школе RealTime
Список интенсивных курсов: [см.]  
  
Специальные предложения: [см.]  
  
 
Блоги Правила Справка Пользователи Календарь Сообщения за день
 

Вернуться   Форум Flasher.ru > Flash > ActionScript 3.0

Версия для печати  Отправить по электронной почте    « Предыдущая тема | Следующая тема »  
Опции темы Опции просмотра
 
Создать новую тему  
Старый 21.10.2013, 14:22
Hauts вне форума Посмотреть профиль Отправить личное сообщение для Hauts Посетить домашнюю страницу Hauts Найти все сообщения от Hauts
  № 1  
Ответить с цитированием
Hauts
 
Аватар для Hauts

блогер
Регистрация: Feb 2008
Адрес: Россия, Новосибирск, Академгородок
Сообщений: 2,113
Записей в блоге: 1
Отправить сообщение для Hauts с помощью ICQ Отправить сообщение для Hauts с помощью Skype™
По умолчанию Простая физика: плиточный мир и ограничение столкновений

Всем привет!

У меня тут случилась вроде бы простая задача, но всё никак не получается решить. Буду благодарен за любую помощь.

В общем, есть некий игровой уровень. Он строится из квадратных "блоков" фиксированного размера (30 пикселей), каждый из которых занимает свою "ячейку", координаты которой кратны размеру. Если проще — подобно тому, как закрашиваем клетки в тетрадном листе.

На этом уровне есть персонаж. Его размер кратен размеру блоков, и он, по-сути, является "склеенными" двумя блоками. В ширину персонах 30 пикселей, в высоту 60. В общем, два "блока".

Персонаж может менять свои координаты, при этом его положение не ограничивается кратностью размера. Он может иметь любые координаты.

Теперь, собственно, к проблеме.

Я не использую каких-либо физических движков (их и не предлагать, кстати). Мне необходимо найти алгоритм ограничения персонажа таким образом, чтобы его габаритная фигура (прямоугольник из двух "блоков") не пересекал клетки и, в ситуациях, когда под персонажем нет клеток (падение), он просто смещался бы вниз (без ускорения достаточно).

Перемещение персонажа считать внешним процессом, никак не зависящим от алгоритма. То есть по факту, при каждой новой итерации алгоритма (энтерфрэйм или таймер, не важно) я должен смотреть, зашел ли персонаж на стенку или "вдавился" ли в пол и, соответственно, разруливать эту ситуацию.

Первое, что приходит на ум — проверка соседних клеток. Если координаты персонажа "входят" в клетку под ним, то сдвинуть его вверх. Если слева от него — сдвинуть вправо и так далее. Но при таком подходе есть неочевидный недостаток, — если персонаж упал на пол, углубился хотя бы чуть-чуть, то его клетками слева и справа станут клетки пола, хотя, по-идее, такого быть не должно. Иными словами, если я падаю с движением вправо на совершенно ровный пол из клеток (блоков), то в какой-то момент меня сдвинет в сторону, как от стены.

Если менять последовательность подобных операций (сравнение сначала по вертикали, потом по горизонтали) — толку особого не будет, лишь поменяется вид ошибки.

Я прикрепил очень простенькую заготовку, если у кого возникнет желание помочь / поразбираться.

PhysTest.swf   (2.5 Кб)


И, конечно же, код:

Код AS3:
package
{
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.ui.Keyboard;
 
	/**
	 * ...
	 * @author Hauts
	 */
	public class Main extends Sprite
	{
		private var _cells:Array;
		private var _cellsX:Array;
		private var _cellSize:int = 30;
		private var _player:Sprite;
		private var _container:Sprite;
		private var _levelWidth:int;
		private var _levelHeight:int;
		private var _up:Boolean = false;
		private var _down:Boolean = false;
		private var _left:Boolean = false;
		private var _right:Boolean = false;
 
		public function Main():void
		{
			if (stage)
			{
				init();
			}
			else
			{
				addEventListener(Event.ADDED_TO_STAGE, init);
			}
		}
 
		private function init(e:Event = null):void
		{
			removeEventListener(Event.ADDED_TO_STAGE, init);
			// entry point
			stage.scaleMode = StageScaleMode.NO_SCALE;
			stage.showDefaultContextMenu = false;
			stage.align = StageAlign.TOP_LEFT;
			_container = new Sprite();
			addChild(_container)
 
			var map:Array = [];
			map.push([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
			map.push([1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1]);
			map.push([1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
			map.push([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]);
 
			_levelWidth = map[0].length * _cellSize;
			_levelHeight = map.length * _cellSize;
 
			// generate map
			_cells = [];
			_cellsX = [];
			for (var k:int = 0; k < map.length; k++)
			{
				var mapColumn:Array = map[k]
				for (var j:int = 0; j < mapColumn.length; j++)
				{
					if (mapColumn[j] != 0)
					{
						var cell:Sprite = createBlock(_cellSize, _cellSize);
						cell.x = _cellSize * j;
						cell.y = _cellSize * k;
						_cells.push(cell)
						if (_cellsX[j] == null)
						{
							_cellsX[j] = [];
						}
						_cellsX[j][k] = cell;
					}
				}
			}
 
			// create 'player' element
			_player = createBlock(_cellSize, _cellSize * 2, 0x00FF00);
			_player.x = _cellSize * 1.25
			_player.y = _cellSize * 2.75
 
			stage.addEventListener(KeyboardEvent.KEY_DOWN, keyHandler);
			stage.addEventListener(KeyboardEvent.KEY_UP, keyHandler);
			addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			enterFrameHandler(null)
		}
 
		private function keyHandler(e:KeyboardEvent):void
		{
			var value:Boolean = e.type == KeyboardEvent.KEY_DOWN;
			var keyCode:uint = e.keyCode;
			if (keyCode == Keyboard.UP)
			{
				_up = value;
			}
			if (keyCode == Keyboard.DOWN)
			{
				_down = value;
			}
			if (keyCode == Keyboard.LEFT)
			{
				_left = value;
			}
			if (keyCode == Keyboard.RIGHT)
			{
				_right = value;
			}
		}
 
		private function enterFrameHandler(e:Event):void
		{
			if (_left)
			{
				_player.x -= 5.125
			}
			if (_right)
			{
				_player.x += 5.125
			}
			if (_up)
			{
				_player.y -= 7.125
			}
			if (_down)
			{
				_player.y += 7.125
			}
			alignToPlayer()
		}
 
		private function alignToPlayer():void
		{
			var sw:int = stage.width;
			var sh:int = stage.height;
			_container.x = sw / 2 - _player.x;
			_container.y = sh / 2 - _player.y;
			if (_container.y > 0)
			{
				_container.y = 0;
			}
			if (_container.x > 0)
			{
				_container.x = 0;
			}
		}
 
		private function createBlock(width:int, height:int, color:uint = 0xFF0000):Sprite
		{
			var blockSprite:Sprite = new Sprite();
			var gr:Graphics = blockSprite.graphics;
			gr.beginFill(color, 0.35);
			gr.lineStyle(1, 0);
			gr.drawRect(0, 0, width, height);
			gr.endFill();
			gr.lineStyle(2, 0);
			gr.moveTo(-1, 0)
			gr.lineTo(1, 0)
			gr.moveTo(0, -1)
			gr.lineTo(0, 1)
			blockSprite.cacheAsBitmap = true;
			_container.addChild(blockSprite)
			return blockSprite;
		}
	}
}
Вложения
Тип файла: swf PhysTest.swf (2.5 Кб, 302 просмотров)
__________________
hauts.ru

Создать новую тему   Часовой пояс GMT +4, время: 18:09.
Быстрый переход
  « Предыдущая тема | Следующая тема »  

Ваши права в разделе
Вы не можете создавать новые темы
Вы не можете отвечать в темах
Вы не можете прикреплять вложения
Вы не можете редактировать свои сообщения

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.


 


Часовой пояс GMT +4, время: 18:09.


Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.