Часть 3. Взаимодействие персонажа с блоками | В паутине

Часть 3. Взаимодействие персонажа с блоками

Вольный перевод статьи How to Create a Platform Game in AS3 – Part 3 Часть комментариев (особенно по математическим расчетам) - моя.

Мы уже разместили все блоки на сцене, придерживаясь схемы, теперь мы можем добавить им некоторую функциональность. Прежде всего, блоки должны быть единственной поверхностью, на которой может стоять наш герой. Чтобы добиться этого, нам необходимо отредактировать функцию mainJump().
Замените этот кусок кода:

//если герой касается пола, движение заканчивается
//позже мы этот код изменим
if(mcMain.y >= stage.stageHeight - mcMain.height){
mainJumping = false;
mcMain.y = stage.stageHeight - mcMain.height;
}

На этот:

//если герой касается блока, движение заканчивается
//этот цикл проверяет столкновение с каждым блоком
for(var i:int = 0;i<blockHolder.numChildren; i++){
	//на каждой итерации получаем текущий блок
	var hitBlock:DisplayObject = blockHolder.getChildAt(i);
	//проверяем столкновение и
	//если герой находится сверху блока
	if(mcMain.hitTestObject(hitBlock) && mcMain.y < hitBlock.y){
		mainOnGround = false;
		//останавливаем движение
		mainJumping = false;
		//устанавливаем координаты, 
		//чтобы герой не провалился в блок
		mcMain.y = hitBlock.y - mcMain.height;
		//выходим из цикла
		break;
	}
}

Отлично, теперь наш персонаж падает вниз, если не находится на блоке. Давайте определим переменную mainOnGround булева типа и присвоим ей по умолчанию значение false.
(Данный код лучше добавить наверх, к другим определениям переменных).
Затем присвоим ей значение true, если персонаж находится на блоке. (Необходимо внести исправления в функцию mainJump() – прим.). Я надеюсь, вы справитесь с этой задачей.
Теперь добавим следующий код в функцию moveChar():

//проверяем, находится ли персонаж на блоке
//цикл проверяет каждый блок на предмет касания
for(var i:int = 0;i<blockHolder.numChildren; i++){
	//в каждой итерации получаем текущий блок
	var hitBlock:DisplayObject = blockHolder.getChildAt(i);
	//если есть столкновение с героем
	//и герой находится сверху
	if(mcMain.hitTestObject(hitBlock) && mcMain.y < hitBlock.y){
		//изменяем состояние героя – «на земле»
		mainOnGround = true;
		//выходим из цикла
		break;
	}
 	//иначе устанавливаем состояние «не на земле»
	mainOnGround = false;
}
 
//если персонаж не на земле
//значит, он в прыжке (подпрыгивает или падает)
if(!mainOnGround){
	mainJumping = true;
}

Следует продумать еще одну вещь. Если персонаж касается блока снизу, он должен отскакивать от него. Иначе он проскочит блок насквозь и окажется сверху, а этого мы никак не можем допустить. Чтобы сделать это, необходимо отредактировать цикл for внутри функции mainJump(). Вот так выглядит окончательный на данный момент код:

for(var i:int = 0;i<blockHolder.numChildren; i++){ //на каждой итерации получаем текущий блок var hitBlock:DisplayObject = blockHolder.getChildAt(i); //проверяем столкновение и if(mcMain.hitTestObject(hitBlock)){ //если герой падает if(jumpSpeed > 0){
			//останавливаем движение
			mainJumping = false;
			//устанавливаем координаты, 
			//чтобы герой не провалился в блок
			mcMain.y = hitBlock.y - mcMain.height;
			//герой «на земле»
			mainOnGround = true;
			//выходим из цикла
			break;
		}else{
		//если герой поднимается вверх
			//делаем его скорость прыжка положительной
			jumpSpeed = Math.abs(jumpSpeed);
			//устанавливаем координаты, чтобы
			//персонаж визуально не прошел сквозь блок
			mcMain.y = hitBlock.y + hitBlock.height + 1;
		}

	}
}

Если вы сейчас запустите наш платформер, то увидите, что герой отскакивает, когда натыкается снизу на блок.
Еще одна задача, которую мы должны решить, - это движение окружающего героя мира, если он отходит слишком далеко влево или вправо. Давайте изменим нашу схему уровня:

var lvlArray1:Array = new Array(
	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,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,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,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,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,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,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,1,1,1,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,1,1,1,1,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,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,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,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,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,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,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,X,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,0,
	0,0,1,1,1,1,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
);

С этого момента наш мир перестал вмещаться в пределы сцены. Теперь необходимо отредактировать функцию createLvl и привязать центр сцены к персонажу. Измененный код:

function createLvl():void{
	//получаем массив текущего уровня
	var lvlArray:Array = MovieClip(root)['lvlArray'+lvlCurrent];
	//наша игровая область состоит из 16 рядов (rows)
	//исходя из этого обрабатываем массив
	//узнаем количество вертикальных колонок
	var lvlColumns:int = Math.ceil(lvlArray.length/16);
	//начинаем создавать уровень
	//проходим в цикле все элементы массива
	for(var i:int = 0;i<lvlArray.length;i++){
		//проверяем, не закончен ли ряд
		//если закончен, переходим к следующему
		if(i/lvlColumns == int(i/lvlColumns)){
			row ++;
		}
		//если значение равно 1 – рисуем блок
		if(lvlArray[i] == 1){
			//создаем новый блок
			var newBlock:Block = new Block();
			//рисуем его
			newBlock.graphics.beginFill(0xFFFFFF);
			newBlock.graphics.drawRect(0,0,25,25);
			//устанавливаем координаты
			newBlock.x = (i-(row-1)*lvlColumns)*newBlock.width;
			newBlock.y = (row-1)*newBlock.height;
			//добавляем в контейнер для блоков
			//в результате блок отображается на сцене
			blockHolder.addChild(newBlock);
		} else if (lvlArray[i] == 'MAIN'){
		//если значение элемента массива MAIN
		//размещаем главного героя
			mcMain.x = (i-(row-1)*lvlColumns)*newBlock.width;
			mcMain.y = (row-1)*newBlock.height;
		}
	}
	//уровень построен, обнуляем счетчик рядов
	row = 0;
	//центрируем экран относительно главного героя
	//определяем, на сколько пикселей необходимо сдвинуть мир
	//250 – середина рабочей области
	var mainMove:int = 250 - mcMain.x;
	//двигаем персонажа и перемещаем весь мир
	mcMain.x += mainMove;
	blockHolder.x += mainMove;
}

Мне очень нравится наш код. Теперь экран отцентрован относительно персонажа на любом уровне, независимо от того, где вы его поместите. Теперь было бы неплохо сделать так, чтобы текущий фон двигался вместо нашего героя, когда он движется, чтобы мы могли все время его видеть. Это очень просто. В функции moveChar() изменим этот фрагмент:

if(leftKeyDown){
	mcMain.x -= mainSpeed;
}
if(rightKeyDown){
	mcMain.x += mainSpeed;
}
На вот этот:
if(leftKeyDown){
	blockHolder.x += mainSpeed;
}
if(rightKeyDown){
	blockHolder.x -= mainSpeed;
}

Очень просто и изящно, не правда ли? И осталась только одна проблема, которую хотелось бы решить до конца этого урока. Наш персонаж прыгает недостаточно высоко. Чтобы исправить это, немного изменим функцию mainJump():
Исходный фрагмент:

//если персонаж еще движется вверх
if(jumpSpeed < 0){ //скорость постепенно уменьшается (по модулю) //-15;-12;-9,6;-7,68… jumpSpeed *= 1 - jumpSpeedLimit/75; //когда она уменьшится до определенной величины (-3) if(jumpSpeed > -jumpSpeedLimit/5){
        //меняем ее знак
        //начинается движение вниз
        jumpSpeed *= -1;
    }
}
//персонаж уже падает, скорость падения не превышает лимит
if(jumpSpeed > 0 && jumpSpeed <= jumpSpeedLimit){
    //скорость падения увеличивается
    //пока не достигнет предельной
    jumpSpeed *= 1 + jumpSpeedLimit/50;
}
//координата персонажа меняется 
//на величину вычисленной скорости
mcMain.y += jumpSpeed;
Отредактированный фрагмент:
//если персонаж еще движется вверх
if(jumpSpeed < 0){ //скорость постепенно уменьшается (по модулю) //-13,2; -11,616; -10,222… jumpSpeed *= 1 - jumpSpeedLimit/125; //когда она уменьшится до определенной величины (-3) if(jumpSpeed > -jumpSpeedLimit/5){
        //меняем ее знак
        //начинается движение вниз
        jumpSpeed *= -1;
    }
}
//персонаж уже падает, скорость падения не превышает лимит
if(jumpSpeed > 0 && jumpSpeed <= jumpSpeedLimit){
    //скорость падения увеличивается
    //пока не достигнет предельной
    jumpSpeed *= 1 + jumpSpeedLimit/50;
}
//координата персонажа меняется 
//на величину вычисленной скорости
mcMain.y += jumpSpeed;

А еще можно изменить jumpSpeedLimit, например, на 20. Итак, мы написали достаточно сложный блок кода. На следующем уроке мы добавим в наш уровень несколько модификаций, например, лестницы и трамплины. Также мы сможем останавливать персонажа, если он столкнется со стеной.

Все уроки руководства

1. Создание главного персонажа
2. Создание уровня
3. Взаимодействие персонажа с блоками
4. Добавление элементов уровня
5. Добавление врагов
6. Завершение уровня
7. Последние штрихи

Комментарии (0)

Ваш email не будет опубликован. Все поля обязательны