Какие виды игр больше всего любите вы: лабиринты, полосы препятствий или что-то еще? Сегодня расскажем, как сделать 2D-платформер в Unity — это жанр, в котором необходимо путешествовать по уровням. Играть очень увлекательно, а разрабатывать еще интереснее — покажем, как юным программистам создать такую игру на базе Unity.
На YouTube-канале школы «Пиксель» вы найдете множество уроков по Unity для начинающих, а также другие плейлисты, которые пригодятся для дальнейшего развития в программировании или подготовке к экзаменам.

Подробную видео инструкцию к материалу вы найдете в конце статьи — рекомендуем сначала ознакомится с теорией, а потом посмотреть, как задача реализуется на практике. Также можно ознакомиться и с другими уроками по Unity для начинающих, чтобы смело приступить к реализации своего проекта в программе и не бояться придумывать что-то новое.

Перед началом создания 2D-платформера на Unity запускаем программу и создаем новый двухмерный проект. Присваиваем ему имя и нажимаем Create Project.

Разработка 2D игры — Эпизод # 29 КОНСТРУКТОР (0.2) #Майнкрафтв2d #2дмайнкрафт #theoryofeverything

Спрайты для игры можно нарисовать самостоятельно или найти готовые в интернете, например, по ссылке Download the latest indie games — itch.io.
Нам понадобятся спрайты персонажа, платформ и фона, скачиваем их и импортируем в наш проект.

С помощью спрайта земли соберем одну большую платформу. Для удобства переместим окно игры рядом с окном сцены. Благодаря комбинации клавиш CTRL+D дублируем блок земли. Если при перетаскивании зажимать клавишу CTRL, то перемещение будет производиться по невидимой сетке — дублируем до тех пор, пока не сделаем необходимую платформу. Создадим пустой объект и поместим в него все блоки, теперь платформу можно перемещать и масштабировать целиком.

Создаем Canvas (полотно), на котором разместим задний фон. Масштаб полотна зададим по размеру экрана, для этого установим разрешение вручную.

Уроки Unity для школьников: как увеличить платформу

Для заднего фона импортируем структуры, которые были найдены в интернете. Далее понравившуюся текстуру устанавливаем в качестве фона и выбираем для нее цвет. Теперь установим тип изображения. Параметр Tiled равномерно покроет заданной текстурой все пространство, не растягивая ее, а именно повторяя. Позицию текстуры установим по всей ширине и высоте.

Выбираем этот параметр с зажатой клавишей Alt. Немного изменим цвет. Установим камеру для полотна и таким образом переместим все игровые объекты на передний план.

Переходим в папку со спрайтами персонажа. Возьмем первый кадр из анимации IDLE и переместим на сцену. Увеличиваем персонажа и убираем размытие у всей его анимации. Это необходимо сделать у всех пиксельных спрайтов.

Добавление Box Collider 2D-платформе в Unity

Создадим папку для анимаций и аниматор в ней для построения логики перехода между анимациями.

Теперь делаем непосредственно самого игрока и переместим в него спрайт.

Открываем окно анимации и перемещаем в удобное место. Создаем анимацию IDLE и сохраняем в ранее созданную папку для анимаций. Выделяем все кадры и перемещаем.

  • растянуть ее в этом окне;
  • поменять значение скорости в аниматоре.

Создание анимации бега в Unity
Проверяем переход между анимацией покоя и бега.

Далее сделаем обратный переход и у каждого перехода уберем галочку с Has Exit Time. Устанавливаем продолжительность перехода 0. Данные параметры нужны для плавного перехода между анимациями, что в данном случае не требуется.

Теперь создадим булевую переменную isMoving, которая будет переключать персонажа из состояния покоя в движение и обратно. Условием для перехода здесь будет isMoving равно true. Переход из бега в покой isMoving равно false. Запустим и проверим переключение анимаций.

Для игрока сделаем капсульный 2D-Collider с настройкой размеров. Тут важно не путать его с обычным, который предназначен для 3D-объектов.

Написание скриптов в Unity

Создадим для этого отдельную папку. Обозначаем скрипт. Удаляем комментарии.

Добавим переменную для задания скорости движения игрока и вектор, задающий направление перемещения, а также ссылку на компонент rigidbody. В функции старт присвоим этот компонент в переменную. Функция старт выполняется один раз при запуске игры, в отличие от функции Update — она вызывается каждый кадр игры. В этой функции мы вызовем функцию Move, отвечающую за движение персонажа.

Читайте также:  Как сделать установить в Майнкрафте

Функция Move начнется с получения направления движения с клавиатуры. Horizontal означает, что мы считываем клавиши a и d, а потом в соответствии с этим сохраняем в Input число от -1 до 1. Чем дольше мы нажимаем клавишу а, тем ближе число подбирается к -1, соответственно и персонаж быстрее двигается в левую сторону. С правой стороной тоже самое, только нажимать нужно клавишу d.
Далее к координатам игрока прибавляем перемещение, которое рассчитывается по формуле: направление умножается на скорость и на время между кадрами игры. Скорость, умноженная на время — это расстояние, которое мы пройдем персонажем в сторону направления вектора input.

Создадим ссылку на компонент SpriteRenderer. Чтобы поворачивать нашего персонажа по направлению движения, добавим условие в Move. Если направление не равно нулю, то есть мы перемещаем персонажа, то поворачиваем спрайт в соответствующую сторону. Вложенное условие как раз и нужно для того, чтобы активировать поворот, если мы повернем персонажа.

Сделаем Sprite Renderer публичной переменной, чтобы поместить в нее ссылку на Sprite Renderer персонажа. Перемещаем спрайт в это поле. Запускаем и проверяем. Поворот работает.

Создание скрипта в Unity для управления переключением анимаций

  1. переменная, в которой будет храниться ссылка на аниматор;
  2. переменная, которая отвечает за включение и отключение анимации бега.
  • если направление не равно нулю, то есть движение;
  • если значение направление равно нулю, то положение не меняется.

Запускаем и проверяем.

Аналогичным образом в игру добавляются и другие анимации: чем больше механик игры, тем сложнее будет логика для их реализации.

Мы завершили создание 2D-платформера на Unity. Теперь вы можете экспериментировать самостоятельно, например, менять персонажа, среду или пробовать различную скорость переходов. Видеоинструкция “Как сделать 2D-платформер на Unity” доступна по ссылке — смотрите и создавайте свою игру.

На нашем YouTube-канале доступны и другие бесплатные уроки по Unity, которые помогут познакомиться с программой и сделать первые шаги. Чтобы досконально разобраться в кодинге и создании игр, стоит обратить внимание на специализированные курсы. Школа программирования «Пиксель» предлагает занятия Unity для школьников с 10 лет — каждый ребенок сможет попробовать свои силы в разработке и защитить собственный проект. Окунитесь в мир IT с профессиональными программистами!

Скрипт PlayerAnimation

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

void Update()
Move();
CheckGround();
if (Input.GetKeyDown(KeyCode.Space))
if (isGrounded)
Jump();
animations.Jump();
>
>
animations.IsMoving = IsMoving;
animations.IsFlying = IsFlying();
>

>
else IsMoving = false;
animations.IsMoving = IsMoving;
>
private void Jump()
Debug.Log(«Jump»);
rb.AddForce(transform.up * jumpForce, ForceMode2D.Impulse);
>

Администрация проекта learn-free.site «Учитесь бесплатно»

ОПИСАНИЕ:

Создание игры 2D Minecraft на C# и WinForms. Мощная практика для начинающих разработчиков игр.

Начинаете изучать программирование на C# и разработку игр? Хотите получить практику на интересном примере? Тогда этот курс специально для вас! Я проводил онлайн курс, где работал с полными новичками. Курс начинался с основ языка: переменные, циклы и функции. И в конце всех ждала практика на примере создания клона игры Minecraft на Winforms.

И практика получилась настолько крутая, что из записей занятий где мы занимались непосредственно Minecraft я сделал этот курс.

В результате у нас получился интересный проект, в котором можно:

Всё это мы сделали без каких либо движков и даже придумали свой формат файлов сохранения, что конечно же очень полезно новичкам для понимания базовых принципов программирования.

Для кого этот курс:

  • Начинающим программистам
  • Начинающим разработчикам игр

Требования

  • Основы C#
  • Visual Studio 2017
  • Windows
Читайте также:  Как Майнкрафт перенести на другой диск ПК

Чему вы научитесь

  • Основы Winforms
  • Основные игровые паттерны
  • Алгоритмы процедурной генерации
  • Сохранение данных игры в файл

Материалы курса

  1. Введение в WinForms на практических примерах
  2. Ядро игры, паттерн цикл обновления и система камер
  3. Постройка блоков, генерация мира, сохранение и загрузка мира
  4. Персонаж, физика и удаление не присоединенных блоков
  5. Работа с файлами и текстом на примере программы фильтрации слов из файла

Источник: learn-free.site

Как создавать деревья или другие структуры из кусков в 2D-игре, подобной Minecraft

Я пытаюсь создать 2D-игру в стиле Minecraft, и у меня возникла проблема. Я пытаюсь создать деревья поверх ландшафта. Рельеф создается с помощью симплексного шума и разбивается на фрагменты 8×8, при необходимости игра генерирует новые фрагменты при каждом движении игрока.

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

Это код для генерации новых чанков, где параметры x и y являются местоположением чанка для генерации:

def generate_chunk(x, y): chunk_data = <> for y_pos in range(CHUNK_SIZE): for x_pos in range(CHUNK_SIZE): block = (x * CHUNK_SIZE + x_pos, y * CHUNK_SIZE + y_pos) block_name = «» height = int(noise.noise2d(block[0]*0.1, 0)*5) if block[1] == 5-height: block_name = «grass_block» elif 5-height < block[1] < 10-height: block_name = «dirt» elif block[1] >= 10-height: block_name = «stone» if block_name != «»: chunk_data[block] = block_name return chunk_data

Вот основной цикл, в котором генерируются фрагменты рядом с игроком, которые временно удаляются и сохраняются, когда игрок уходит:

running = True while running: dt = clock.tick(FPS) / 16 pygame.display.set_caption(f»2D Minecraft | FPS: «) for event in pygame.event.get(): if event.type == QUIT: running = False rendered_chunks = [] for y in range(int(HEIGHT/(CHUNK_SIZE*BLOCK_SIZE)+2)): for x in range(int(WIDTH/(CHUNK_SIZE*BLOCK_SIZE)+2)): chunk = ( x — 1 + int(round(camera.pos.x / (CHUNK_SIZE * BLOCK_SIZE))), y — 1 + int(round(camera.pos.y / (CHUNK_SIZE * BLOCK_SIZE))) ) rendered_chunks.append(chunk) if chunk not in chunks: chunks[chunk] = Chunk(chunk) unrendered_chunks = [] for y in range(int(HEIGHT/(CHUNK_SIZE*BLOCK_SIZE)+4)): for x in range(int(WIDTH/(CHUNK_SIZE*BLOCK_SIZE)+4)): chunk = ( x — 2 + int(round(camera.pos.x / (CHUNK_SIZE * BLOCK_SIZE))), y — 2 + int(round(camera.pos.y / (CHUNK_SIZE * BLOCK_SIZE))) ) try: chunks[chunk] except: pass else: if chunk not in rendered_chunks: unrendered_chunks.append(chunk) for chunk in unrendered_chunks: for block in chunks[chunk].block_data: if block in blocks: blocks[block].kill() del blocks[block] camera.update() player.update() screen.fill((135, 206, 250)) for chunk in rendered_chunks: chunks[chunk].render() player.draw(screen) pygame.display.flip()

Вот класс Block и класс Chunk:

class Block(pygame.sprite.Sprite): def __init__(self, chunk, pos, name): pygame.sprite.Sprite.__init__(self) blocks[tuple(pos)] = self self.name = name self.chunk = chunk self.coords = vec(pos) self.pos = self.coords * BLOCK_SIZE self.image = block_textures[self.name] self.rect = self.image.get_rect() def update(self): self.rect.topleft = self.pos — camera.pos def draw(self, screen): screen.blit(self.image, self.rect.topleft) class Chunk(object): def __init__(self, pos): self.pos = pos self.block_data = generate_chunk(pos[0], pos[1]) for block in self.block_data: blocks[block] = Block(self, block, self.block_data[block]) def render(self): if self.pos in rendered_chunks: for block in self.block_data: try: blocks[block] except: blocks[block] = Block(self, block, self.block_data[block]) blocks[block].update() blocks[block].draw(screen) pygame.draw.rect(screen, (255, 255, 0), (self.pos[0]*CHUNK_SIZE*BLOCK_SIZE-camera.pos[0], self.pos[1]*CHUNK_SIZE*BLOCK_SIZE-camera.pos[1], CHUNK_SIZE*BLOCK_SIZE, CHUNK_SIZE*BLOCK_SIZE), width=1)

Минимальный воспроизводимый код, я думаю, вся необходимая информация будет выше, но на всякий случай вам понадобится остальное:

import pygame from pygame.locals import * from random import * from math import * import json import os import opensimplex FPS = 60 WIDTH, HEIGHT = 1200, 600 SCR_DIM = (WIDTH, HEIGHT) GRAVITY = 0.5 SLIDE = 0.3 TERMINAL_VEL = 24 BLOCK_SIZE = 64 CHUNK_SIZE = 8 SEED = randint(-2147483648, 2147483647) pygame.init() screen = pygame.display.set_mode((WIDTH, HEIGHT), HWSURFACE | DOUBLEBUF) pygame.display.set_caption(«2D Minecraft») clock = pygame.time.Clock() mixer = pygame.mixer.init() vec = pygame.math.Vector2 noise = opensimplex.OpenSimplex(seed=SEED) seed(SEED) block_textures = <> for img in os.listdir(«res/textures/blocks/»): block_textures[img[:-4]] = pygame.image.load(«res/textures/blocks/»+img).convert_alpha() for image in block_textures: block_textures[image] = pygame.transform.scale(block_textures[image], (BLOCK_SIZE, BLOCK_SIZE)) def intv(vector): return vec(int(vector.x), int(vector.y)) def inttup(tup): return (int(tup[0]), int(tup[1])) def block_collide(ax, ay, width, height, b): a_rect = pygame.Rect(ax-camera.pos.x, ay-camera.pos.y, width, height) b_rect = pygame.Rect(b.pos.x-camera.pos.x, b.pos.y-camera.pos.y, BLOCK_SIZE, BLOCK_SIZE) if a_rect.colliderect(b_rect): return True return False class Camera(pygame.sprite.Sprite): def __init__(self, master): self.master = master self.pos = self.master.size / 2 self.pos = self.master.pos — self.pos — vec(SCR_DIM) / 2 + self.master.size / 2 def update(self): tick_offset = self.master.pos — self.pos — vec(SCR_DIM) / 2 + self.master.size / 2 if -1 < tick_offset.x < 1: tick_offset.x = 0 if -1 < tick_offset.y < 1: tick_offset.y = 0 self.pos += tick_offset / 10 class Player(pygame.sprite.Sprite): def __init__(self): pygame.sprite.Sprite.__init__(self) self.size = vec(0.225*BLOCK_SIZE, 1.8*BLOCK_SIZE) self.width, self.height = self.size.x, self.size.y self.start_pos = vec(0, 3) * BLOCK_SIZE self.pos = vec(self.start_pos) self.coords = self.pos // BLOCK_SIZE self.vel = vec(0, 0) self.max_speed = 5.306 self.jumping_max_speed = 6.6 self.rect = pygame.Rect((0, 0, 0.225*BLOCK_SIZE, 1.8*BLOCK_SIZE)) self.bottom_bar = pygame.Rect((self.rect.x+1, self.rect.bottom), (self.width-2, 1)) self.on_ground = False def update(self): keys = pygame.key.get_pressed() if keys[K_a]: if self.vel.x >-self.max_speed: self.vel.x -= SLIDE elif self.vel.x < 0: self.vel.x += SLIDE if keys[K_d]: if self.vel.x < self.max_speed: self.vel.x += SLIDE elif self.vel.x >0: self.vel.x -= SLIDE if keys[K_w] and self.on_ground: self.vel.y = -9.2 self.vel.x *= 1.1 if self.vel.x > self.jumping_max_speed: self.vel.x = self.jumping_max_speed elif self.vel.x < -self.jumping_max_speed: self.vel.x = -self.jumping_max_speed if -SLIDE < self.vel.x < SLIDE: self.vel.x = 0 self.vel.y += GRAVITY if self.vel.y >TERMINAL_VEL: self.vel.y = TERMINAL_VEL self.move() self.bottom_bar = pygame.Rect((self.rect.left+1, self.rect.bottom), (self.width-2, 1)) for block in blocks: if self.bottom_bar.colliderect(blocks[block].rect): self.on_ground = True break else: self.on_ground = False if self.on_ground: self.vel.x *= 0.99 self.coords = self.pos // BLOCK_SIZE self.chunk = self.coords // CHUNK_SIZE self.rect.topleft = self.pos — camera.pos def draw(self, screen): pygame.draw.rect(screen, (0, 0, 0), self.rect) def move(self): for y in range(4): for x in range(3): try: block = blocks[(int(self.coords.x-1+x), int(self.coords.y-1+y))] except: pass else: if self.vel.y < 0: if block_collide(floor(self.pos.x), floor(self.pos.y+self.vel.y), self.width, self.height, block): self.pos.y = floor(block.pos.y + BLOCK_SIZE) self.vel.y = 0 elif self.vel.y >= 0: if self.vel.x 0: if block_collide(ceil(self.pos.x), ceil(self.pos.y+self.vel.y), self.width, self.height, block): self.pos.y = ceil(block.pos.y — self.height) self.vel.y = 0 if self.vel.x < 0: if block_collide(floor(self.pos.x+self.vel.x), floor(self.pos.y), self.width, self.height, block): self.pos.x = floor(block.pos.x + BLOCK_SIZE) self.vel.x = 0 elif self.vel.x >= 0: if block_collide(ceil(self.pos.x+self.vel.x), ceil(self.pos.y), self.width, self.height, block): self.pos.x = ceil(block.pos.x — self.width) self.vel.x = 0 self.pos += self.vel class Block(pygame.sprite.Sprite): def __init__(self, chunk, pos, name): pygame.sprite.Sprite.__init__(self) blocks[tuple(pos)] = self self.name = name self.chunk = chunk self.coords = vec(pos) self.pos = self.coords * BLOCK_SIZE self.image = block_textures[self.name] self.rect = self.image.get_rect() def update(self): self.rect.topleft = self.pos — camera.pos def draw(self, screen): screen.blit(self.image, self.rect.topleft) class Chunk(object): def __init__(self, pos): self.pos = pos self.block_data = generate_chunk(pos[0], pos[1]) for block in self.block_data: blocks[block] = Block(self, block, self.block_data[block]) def render(self): if self.pos in rendered_chunks: for block in self.block_data: try: blocks[block] except: blocks[block] = Block(self, block, self.block_data[block]) blocks[block].update() blocks[block].draw(screen) pygame.draw.rect(screen, (255, 255, 0), (self.pos[0]*CHUNK_SIZE*BLOCK_SIZE-camera.pos[0], self.pos[1]*CHUNK_SIZE*BLOCK_SIZE-camera.pos[1], CHUNK_SIZE*BLOCK_SIZE, CHUNK_SIZE*BLOCK_SIZE), width=1) def generate_chunk(x, y): chunk_data = <> for y_pos in range(CHUNK_SIZE): for x_pos in range(CHUNK_SIZE): block = (x * CHUNK_SIZE + x_pos, y * CHUNK_SIZE + y_pos) block_name = «» height = int(noise.noise2d(block[0]*0.1, 0)*5) if block[1] == 5-height: block_name = «grass_block» elif 5-height < block[1] < 10-height: block_name = «dirt» elif block[1] >= 10-height: block_name = «stone» if block_name != «»: chunk_data[block] = block_name return chunk_data blocks = <> chunks = <> player = Player() camera = Camera(player) running = True while running: dt = clock.tick(FPS) / 16 pygame.display.set_caption(f»2D Minecraft | FPS: «) for event in pygame.event.get(): if event.type == QUIT: running = False rendered_chunks = [] for y in range(int(HEIGHT/(CHUNK_SIZE*BLOCK_SIZE)+2)): for x in range(int(WIDTH/(CHUNK_SIZE*BLOCK_SIZE)+2)): chunk = ( x — 1 + int(round(camera.pos.x / (CHUNK_SIZE * BLOCK_SIZE))), y — 1 + int(round(camera.pos.y / (CHUNK_SIZE * BLOCK_SIZE))) ) rendered_chunks.append(chunk) if chunk not in chunks: chunks[chunk] = Chunk(chunk) unrendered_chunks = [] for y in range(int(HEIGHT/(CHUNK_SIZE*BLOCK_SIZE)+4)): for x in range(int(WIDTH/(CHUNK_SIZE*BLOCK_SIZE)+4)): chunk = ( x — 2 + int(round(camera.pos.x / (CHUNK_SIZE * BLOCK_SIZE))), y — 2 + int(round(camera.pos.y / (CHUNK_SIZE * BLOCK_SIZE))) ) try: chunks[chunk] except: pass else: if chunk not in rendered_chunks: unrendered_chunks.append(chunk) for chunk in unrendered_chunks: for block in chunks[chunk].block_data: if block in blocks: blocks[block].kill() del blocks[block] camera.update() player.update() screen.fill((135, 206, 250)) for chunk in rendered_chunks: chunks[chunk].render() player.draw(screen) pygame.display.flip() pygame.quit() quit()

Читайте также:  Как добавить босс бар в Майнкрафт

(между желтыми линиями — границы чанков)

Источник: question-it.com