Blenderonacid Release V4 скоро появится и будет включать полный исходный код Minimario для образовательных целей. В этом посте я расскажу, как работает большая часть этой демонстрации, вы узнаете о: Tpython, JavaScript, ODE, BPY и межпроцессной связи.
Blender имеет свой собственный встроенный текстовый редактор, а формат файла .blend может содержать текстовые объекты, на которые можно ссылаться другие объекты. Выше всего демонстрируется исходный код, наряду с сгенерированными 3D -данными. Чтобы узнать, как что -то работает, это помогает все это в одном файле .blend.
Лично мне не нравится писать код в текстовом редакторе Blender, и вместо этого предпочитаю полностью управляемый данными стиль, где я генерирую единый файл .blend из другого скрипта Python, этот скрипт генератора устанавливает сцену, создает текстовые объекты и ссылки их все вместе.
import bpy, os, sys, math thispath = os.path.split(__file__)[0] world = bpy.data.worlds[0] world['threejs_canvas'] = None script = bpy.data.texts.new(name='minimario_main') world.threejs_script = script src = open(os.path.join(thispath,'minimario/minimario_escapes_main.py'),'rb').read().decode('utf-8') script.from_string( src ) js = bpy.data.texts.new(name='minimario_escapes_js') src = open(os.path.join(thispath,'minimario/minimario_escapes_js.py'),'rb').read().decode('utf-8') js.from_string( src ) script.include0 = js shared = bpy.data.texts.new(name='minimario_shared') src = open(os.path.join(thispath,'minimario/minimario_shared.py'),'rb').read().decode('utf-8') shared.from_string( src ) script.include1 = shared draw = bpy.data.texts.new(name='minimario_draw') src = open(os.path.join(thispath,'minimario/minimario_draw.py'),'rb').read().decode('utf-8') draw.from_string( src ) script.include2 = draw blender_start = bpy.data.texts.new(name='minimario_startup') world.blender_startup_script = blender_start blender_start.include0 = shared src = open(os.path.join(thispath,'minimario/minimario_startup.py'),'rb').read().decode('utf-8') blender_start.from_string( src ) onupdate = ''' import math M = bpy.data.objects['mario_collision'] MR = bpy.data.objects['marioroot'] if M.location[0] + 0.1 < M['px']: if MR.rotation_euler.z < math.radians( 180 ): MR.rotation_euler.z += 0.5 else: MR.rotation_euler.z *= 0.75 M['px'] = M.location[0] if M.location.x > 9 and M.location.z > 8 and M.location.z < 13: bname = 'brick-%s' % int(M.location.x) if bname not in bpy.data.objects: bname = 'brick-%s' % int(M.location.x+0.5) if bname not in bpy.data.objects: bname = 'brick-%s' % int(M.location.x-0.5) if bname in bpy.data.objects: brick = bpy.data.objects[ bname ] if 'broken' not in brick.keys(): print('breaking new brick') brick['broken'] = True bpy.ops.ode.cell_fracture(object_name=bname) ''' blender_onupdate = bpy.data.texts.new(name='minimario_onupdate') world.blender_script = blender_onupdate blender_onupdate.from_string(onupdate)
Выше вы можете видеть, что в основном этот скрипт просто считывает другие сценарии Python из подразделения и создает текстовые объекты. Некоторый код является встроенным, сценарий onupdate, он называется каждым кадром в блендере.
Выше Марио начинается в окне Emscripten | SDL, которое он прыгает в отладчик Emscripten, переходя от SDL, втянутых пикселей в искусство CSS ASCII, когда он покидает окно веб -браузера и входит в блендер, он превращается в 3D ASCII искусство. Затем он ломает несколько блоков, используя физику ODE и мой сценарий настраиваемого перелома ячеек. Наконец он возвращается в окно браузера и сбивает окно SDL снизу и прыгает обратно в окно SDL.
CSS Mario просто нарисован под окном SDL, его положение синхронизируется из TPYTHON с этим вызовом функции.
set_ascii_mario( B.getPosition()[0]-60, -float(B.getPosition()[1]+320), state["direction"] )
Переменная B
это тело ODE, используемое для моделирования его физики. Определение set_ascii_mario
ниже.
def set_ascii_mario(x,y, direction): bump *= 0.75 canvas.style.top = (75 - bump) + 'px' if direction == 1: marioR.hidden = true mariodiv.hidden = false mariodiv.style.left = (x + canvas.offsetLeft) + 'px' mariodiv.style.top = y + 'px' else: mariodiv.hidden = true marioR.hidden = false marioR.style.left = (x + canvas.offsetLeft) + 'px' marioR.style.top = y + 'px'
Выше вы, вероятно, ожидали увидеть JavaScript вместо Python. В то время как чистый JavaScript может быть смешан с вашим сценарием Python, работающим в скомпилированном интерпретаторе Tpython WASM, предпочтительнее, чтобы вы писали свой код JS в стиле Python и позволили Tpython Toolchain перенести его в JS, это потому, что тогда Функция JS будет автоматически подвергаться вашему сценарию Tpython для легкого вызова.
На этом этапе вам может быть интересно, как позиция Марио синхронизирована с блендером. Это делается с использованием стандартного Xmlhttprequest.
def sendmario(): var msg = [{"name":"mario_collision", "pos":[X,-Z,Y]}] var req = new XMLHttpRequest() req.addEventListener("load", sendmario) req.open("GET", "http://localhost:8080/update?" + JSON.stringify(msg)) req.send() setTimeout(sendmario, 100)
Наверху угоняет встроенный сервер Blenderonacid, обычно используемый для синхронизации трех объектов, но в этом случае мы не используем три. Вместо этого мы создали mario_collision
объект заранее в сценарии запуска блендера, который является родителем 3D ASCII Mario. Ниже приведен сценарий запуска блендера.
import bpy, math, _bpy ob = bpy.data.objects['Cube'] ob.name = 'mario_collision' ob.display_type = "WIRE" ob.ode_size[0] = 2 ob.ode_size[1] = 2 ob.ode_size[2] = 2 ob.ode_kinematic = True _bpy.ode_add_object( ob ) ob['px'] = 0.0 root = bpy.data.objects.new(name='marioroot', object_data=None) root.scale *= 0.12 bpy.context.collection.objects.link(root) root.parent = ob cam = bpy.data.objects['Camera'] cam.location = [10, -30, 5] cam.rotation_euler = [math.radians(90), 0, 0] def make_mario( asciiart, collection, root ): z = 16 for ln in asciiart: x = -16 for c in ln: x += 2 if c == '_': continue pix = bpy.data.objects.new(name='pixel', object_data=bpy.data.meshes['Cube'].copy()) if c not in bpy.data.materials: mat = bpy.data.materials.new(name=c) mat.use_nodes = False if c == '?': r,g,b = MarioPal['%'] else: r,g,b = MarioPal[c] mat.diffuse_color = [r/255,g/255,b/255,1.0] else: mat = bpy.data.materials[c] pix.data.materials[0]=mat collection.objects.link(pix) pix.location = [x,0,z] pix.scale.y = 2 pix.modifiers.new(name='bevel', type="BEVEL") pix.parent=root z -= 2 col = bpy.data.collections.new(name='mariopixels') bpy.context.collection.children.link(col) make_mario( Mario, col, root ) def make_bricks(): mat = bpy.data.materials.new(name='brick') mat.use_nodes = False mat.diffuse_color = [0.57, 0.15, 0.14, 1.0] x = 10 z = 10 for i in range(4): brick = bpy.data.objects.new(name='brick-%s' %int(x), object_data=bpy.data.meshes['Cube'].copy()) bpy.context.collection.objects.link(brick) brick.location = [x, 0, z] brick.data.materials[0] = mat brick.ode_size[0] = 2 brick.ode_size[1] = 2 brick.ode_size[2] = 2 brick.ode_kinematic = True _bpy.ode_add_object( brick ) x += 2 make_bricks()
Выше вы можете видеть, что я настраиваю вторичное симуляцию физики ODE в Blender. Основное моделирование физики ODE фактически работает в веб -браузере, отправляя обновления позиции для mario_collision
к этой физике симуляции здесь, в Блендере. Обратите внимание, что в блендере я делаю эти тела ODE_KINEMATIC
Это означает, что они имеют только столкновение и отвечают на обновления позиций с пользовательского интерфейса или встроенного веб -сервера.
Наконец, давайте рассмотрим код, который запускает кирпичи, чтобы сломаться, это происходит в основном сценарии блендера OnuPdate.
M = bpy.data.objects['mario_collision'] if M.location.x > 9 and M.location.z > 8 and M.location.z < 13: bname = 'brick-%s' % int(M.location.x) if bname not in bpy.data.objects: bname = 'brick-%s' % int(M.location.x+0.5) if bname not in bpy.data.objects: bname = 'brick-%s' % int(M.location.x-0.5) if bname in bpy.data.objects: brick = bpy.data.objects[ bname ] if 'broken' not in brick.keys(): print('breaking new brick') brick['broken'] = True bpy.ops.ode.cell_fracture(object_name=bname)
Почему я обходил систему обратного выявления Callision ODE Callision и пишу уродливый код, как выше, чтобы вызвать перелом ячейки? Потому что это просто, и показывать вам простой трюк для обнаружения столкновений, где вы окружаете позиции, чтобы целые числа и ищите объект по этому округлению. Этот чит работает хорошо, если ваши игровые объекты имеют примерно одинакового размера.
Оригинал: “https://dev.to/djraptor11/blender-on-acid-29fm”