Я пока буксую ...
Люди слухают музон..
https://www.youtube.com/watch?v=ORuMA7C0j3A
Люди что-то делают..
3D Action RPG Remastered Tutorial in Godot 4.2! 8 Hour Free Course!
https://www.youtube.com/watch?v=1m6UXCtw2Pg
Мистер "Coding Quests" учит - как сделать игру (видюшка на 8 часов).
Первые 20 минут - заряжаем набор три-мерных тайлов для GridMap.
Модельки взяты отсюда..
https://kaylousberg.itch.io/kaykit-adventurers
Под видео, есть подробный набор линков на ресурсы.
slatazan
А я ведь надо признаться, хоть мало что понимаю из твоих сообщений, но вдохновился твоей игрой.
И делаю походовую стратегию. Правда у меня сайфай.
Хотя тоже, на уровне мин прототипа подвис. Времени совсем нет.
Der FlugSimulator
// делаю походовую стратегию
Если есть тема - напечатай линк.
Если есть демо - могу поиграть-покритиковать.
Вот есть пошаговая игра..
Примерно с 10-й минуты идёт казуальная игра - вроде-бы, игроку понравилось.
https://www.youtube.com/watch?v=X2MYkCyQn7c
Как-минимум с 20-й по 30-ю минуту - можно посмотреть на трудный стартовый бой.
slatazan
> Если есть тема - напечатай линк.
Пока нет. Я вначале начал делать хексагонами, как у тебя, потом немного переделал.
Потом переделал еще и с очередностью ходов, создав тему.
В общем у меня пока всё в зачаточной стадии и даже не совсем собирается.
Но как только будет - сразу создам тему.
Кое-как собрал скрипт _игрока - некие базовые возможности.
ESC переключает курсор, и если курсор виден, то клик для указания _куда_бежать,
по полигонам нав-меша. Если не видно курсора - жмём кнопки, чтобы двигать
персонажа - ему мешаются ноды колизий, которые принимали участие в запеканиях
двух слоёв нав-меша (где пехота проходит, и где низкий полёт пролетает).
Надо напечатать код на форум, для сохранки :)
extends CharacterBody3D # player # ../Shape3D # ../Knight[ ... AnimationPlayer] # ../camera_base.h.v.Camera3D # ../AnimationTree # ../NavigationAgent3D @onready var anim_tree = get_node("AnimationTree") @onready var playback = anim_tree.get( "parameters/playback") @onready var pl_mesh = get_node( "Knight") @onready var pl_mesh_cape = get_node( "Knight/Rig/Skeleton3D/Knight_Cape") @onready var camera_h = get_node( "camera_base/h") @onready var nav: NavigationAgent3D = get_node( "NavigationAgent3D") @export var gravity := 9.8 @export var jump_force: int = 9 @export var walk_speed: int = 8 @export var run_speed: int = 12 # state machine var is_atk: bool = false # атакуем var is_walk: bool = false var is_run: bool = false # бежым var is_death: bool = false # подыхаем # anim Node Names var nod_nam_idle : String = "Idle" var nod_nam_walk : String = "Walk" var nod_nam_run : String = "Run" var nod_nam_jump : String = "Jump" var nod_nam_atk1 : String = "Attack1" var nod_nam_death : String = "Death_A" # --- const RAY_LENGTH = 1000 const JUMP_VELOCITY = 5.0 var movement_speed := 4.0 # сюда ставить скорость бега, или скорость шага. var try_find_way := false var test_is_fly := false var klik_mouse_mode := 0 var ray_ignor_scn = null # test var pillar_gltf = null func _ready( ) -> void: nav.velocity_computed.connect( Callable( _on_velocity_computed)) #ray_ignor_scn = get_node( "... /GridMap2") #pillar_gltf = get_node( "... /pillar/StaticBody3D") if Input.get_mouse_mode( ) != Input.MOUSE_MODE_CAPTURED: klik_mouse_mode = 1 #--------- func _on_velocity_computed( safe_velocity: Vector3): velocity = safe_velocity move_and_slide( ) #--------- func _physics_process( delta): var on_floor = is_on_floor( ) # узнать - есть-ли контакт, с поверхностью. if not on_floor: # Если персонаж подвис, тогда.. velocity.y -= gravity * delta # Add the gravity. // гравитация давит. #if not is_death -> pass if Input.get_mouse_mode( ) != Input.MOUSE_MODE_CAPTURED: if not klik_mouse_mode: klik_mouse_mode = 1 # первый кадр, после переключения - сбросим старый путь.. if not nav.is_navigation_finished( ): nav_target( self.position) # чтобы обнулить пре-поиск. # move_klik( delta) # тэстируем поинт-клик else: klik_mouse_mode = 0 move_wasd( delta) # рулим движуху ... стрелочками if( nod_nam_atk1 in playback.get_current_node( ) ): is_atk = true else: is_atk = false attack1( ) anim_tree[ "parameters/conditions/IsOnFloor"] = on_floor anim_tree[ "parameters/conditions/IsInAir"] = ! on_floor anim_tree[ "parameters/conditions/IsWalk"] = is_walk anim_tree[ "parameters/conditions/IsNotWalk"] = ! is_walk anim_tree[ "parameters/conditions/IsRun"] = is_run anim_tree[ "parameters/conditions/IsNotRun"] = ! is_run anim_tree[ "parameters/conditions/IsDeath"] = is_death #--------- func nav_target( movement_target: Vector3): nav.set_target_position( movement_target) #--------- func nav_set_land( also_collision: int = 0): nav.set_navigation_layer_value( 1, true) nav.set_navigation_layer_value( 2, false) if also_collision: set_collision_mask_value( 1, true) set_collision_mask_value( 2, false) #--------- func nav_set_fly( also_collision: int = 0): nav.set_navigation_layer_value( 1, false) nav.set_navigation_layer_value( 2, true) if also_collision: set_collision_mask_value( 1, false) set_collision_mask_value( 2, true) #--------- func priv_may_toggle__alt_path( ) -> int: # Вызываем процу от сигнала - попытка сменить полёт-пехоту. # Сбрасываем нынешний путь - он сам сбросится. # Псевдо-клик, как будто под персонажа, и get_final_position( ), # по хикс-зэд, должна быть совсем близко - тогда return 1; # Иначе - точка в другом слое плохо досягаема - сместись, # и снова переключай полёт-пехоту. if test_is_fly: # Сейчас - в полёте, nav_set_land( ) # и нужно проверить место посадки, в первом слое. else: nav_set_fly( ) nav_target( self.position) var v3 = nav.get_final_position( ) v3.y = self.position.y # выровним, чтобы в двумерке была дистанция.. var a = v3.distance_to( self.position) #print( "alt_path dist: ", a) if test_is_fly: # Сейчас - в полёте, вернуть полётный слой.. nav_set_fly( ) else: nav_set_land( ) nav_target( self.position) # чтобы обнулить пре-поиск. # Не знаю, как сбрасывать старый путь. #if a > 0.5: if a > 0.4: # слишком большой допуск, return 0 # и блокируем смену слоя. return 1 # разрешаем смену слоя - капсулу игрока авто-подвинут. #--------- func attack1( ): if( nod_nam_idle in playback.get_current_node( ) ) or( nod_nam_walk in playback.get_current_node( ) ): # or( #nod_nam_run in playback.get_current_node( ) ): // нет атаки, при спринте. pass if Input.is_action_just_pressed( "attack1"): # Эту аниму можно назначить: кастинг сбора простой травы-руды. #print( "strike") if ! is_atk: # Если персонаж не занят атакой - пусть атакует. playback.travel( nod_nam_atk1) # включить аниму _атаки. # --- в целях тэста, добавка для переключения полёт-пехота.. toggle_nav_land_fly( ) #--------- func toggle_nav_land_fly( ): if priv_may_toggle__alt_path( ): # Если можно переключать, тогда.. if test_is_fly: # смотри старое // new --> пехота // первый слой. nav_set_land( 1) test_is_fly = false pl_mesh.position.y = 0.0 pl_mesh_cape.hide( ) else: # // new --> полёт // ставим второй слой. nav_set_fly( 1) test_is_fly = true pl_mesh.position.y = 1.0 pl_mesh_cape.show( ) #---------
func move_wasd(delta): if Input.is_action_just_pressed( "ui_accept") and is_on_floor( ): velocity.y = JUMP_VELOCITY # прыгаем, если сигнал, и чувствуем опору. # Get the input direction and handle the movement/deceleration. # As good practice, you should replace UI actions with custom gameplay actions. var input_dir = Input.get_vector( "ui_left", "ui_right", "ui_up", "ui_down") var direction = ( transform.basis * Vector3( input_dir.x, 0, input_dir.y)).normalized( ) if direction: #velocity.x = direction.x * SPEED #velocity.z = direction.z * SPEED # OR #var v1 = direction.rotated( Vector3( 0, 1.0, 0), deg_to_rad( camera_h.rotation.y)) # оказалось, что не нужно конвертировать градусы в радианы ... # Почитал подсказку - действительно, градусная постановка - для удобства, # а свойство несёт радианы, когда работает. var v1 = direction.rotated( Vector3( 0, 1.0, 0), camera_h.rotation.y) velocity.x = v1.x * movement_speed velocity.z = v1.z * movement_speed var nt = get_node_or_null( "../CanvasLayer/Label") if nt: nt.text = "%1.1f %1.1f rot%1.1f" % [ v1.x, v1.z, camera_h.rotation.y] pl_mesh.rotation.y = camera_h.rotation.y + deg_to_rad( -180.0) walk_run( 1) else: walk_run( 0) velocity.x = move_toward( velocity.x, 0, movement_speed) velocity.z = move_toward( velocity.z, 0, movement_speed) move_and_slide( ) #--------- var auto_shift := 0 # мало знаний про Анимашн, но хакнуть можно быстро. func walk_run( move: int): var run_mode := false # выяснить ( галочка или прижатый шыфт). #if Input.is_key_pressed( KEY_SHIFT): ... # Вероятно, персонаж всегда будет бегать. // но бег не анимируется _сразу, # и надо начинать с шага, чтобы анима бега подхватилась ... if auto_shift == 2: run_mode = true if move and( auto_shift < 1): # 0 auto_shift = 1 else: auto_shift = 2 # if Input.is_key_pressed( KEY_SHIFT): # чтобы шаг был доступен. run_mode = false is_walk = false is_run = false if run_mode: if move: is_run = true else: auto_shift = 0 movement_speed = run_speed else: if move: is_walk = true else: auto_shift = 0 movement_speed = walk_speed # #--------- func _unhandled_input( event): if event is InputEventMouseButton and event.pressed and event.button_index == 1: try_find_way = true # оформить зацепку, которую перехватят в _physics_process #--------- func move_klik( delta): if try_find_way: try_find_way = false var space_state = get_world_3d( ).direct_space_state var cam = get_node( "camera_base/h/v/Camera3D") var mousepos = get_viewport( ).get_mouse_position( ) var origin = cam.project_ray_origin( mousepos) var end = origin + cam.project_ray_normal( mousepos) * RAY_LENGTH var query = PhysicsRayQueryParameters3D.create( origin, end) query.collide_with_areas = true #query.exclude = self # [self, доп] # масив // игнорим свои колиз-шэйпы. #query.exclude = [ self, null] # так нельзя #query.exclude = [ self, ray_ignor_scn] # так не игнорят. # Попытки вытаскивать мешы в m_grid, и добавлять каждую вторую ячейку, # в масив ignor = игнор не работает по Мешам. # Зато стал работать по отдельному статик боди, который выташил # из отдельной заготовки. Значит, можно строить локацию хитрей, # чтобы столбы были отдельно ( на снэпе - это повторит грид-мэп). # Можно делать почти плоские поляны-комнаты. # Есть кнопка PageDown для Node3D: ставит на пол - на первую опору. # Впрочем, кликать на стену, и думать о точке _за_стеной - это # не очевидно ( для разных игроков). Класически - клик по стене - надо # бежать к основанию стены - так и происходит, если стену не игнорить. var ignor = [] ignor.append( self) # игнорит, потому-что CharacterBody3D ( тело). if pillar_gltf: ignor.append( pillar_gltf) # сработала указка на StaticBody3D ( тело). query.exclude = ignor # передали масив игнорирований _тел. var result = space_state.intersect_ray( query) # узнали пересечение. nav_target( result.position) # приказали _навигатору найти путь. print( result.position) # Путь, примерно, к этой точке. elif nav.is_navigation_finished( ): walk_run( 0) return walk_run( 1) var next_p: Vector3 = nav.get_next_path_position( ) var new_v: Vector3 = global_position.direction_to( next_p) * movement_speed if nav.avoidance_enabled: nav.set_velocity( new_v) # Чуток потэстил - не хочу эту огибаловку использовать. # Пусть игроки и монстры пробегают сквозь друг-друга. else: _on_velocity_computed( new_v) var v1 = Vector3( 0, 0, 1) # позитивная единица, вместо негативной. var ug = v1.signed_angle_to( new_v, Vector3( 0, 1, 0)) pl_mesh.rotation.y = ug #--------- #.
slatazan
> Надо напечатать код на форум, для сохранки
Контроль версий не используешь что ли?
Я конечно не против, пиши, мог бы даже code review сделать, у тебя тут даже на поверхности совсем чудеса, но...
Всё таки это форум, тут бы лучше на человекопонятном языке писать ))
Der FlugSimulator
// Контроль версий
Угу, не использую. Считаю, что мне - повезёт :)
// совсем чудеса, в коде
Ну, не знаю. Геройчик бегает нормально.
Мини-скрипт, который для поворота камеры использую..
extends Node3D var h_sens: float = 0.003 # 0.005 // карусель-поворот var h_accel: float = 8.0 # малое значение - большая энэрция var kh := 0.0 var kv := -35.0 func _ready(): Input.set_mouse_mode( Input.MOUSE_MODE_CAPTURED) func _input( e: InputEvent): if e is InputEventMouseMotion and( Input.get_mouse_mode( ) == Input.MOUSE_MODE_CAPTURED): kh += -e.relative.x * h_sens # карусель #kv += -e.relative.y * v_sens # кивок if e is InputEventKey: if e.is_action_pressed( "ui_cancel"): if Input.get_mouse_mode( ) != Input.MOUSE_MODE_CAPTURED: Input.set_mouse_mode( Input.MOUSE_MODE_CAPTURED) else: Input.set_mouse_mode( Input.MOUSE_MODE_VISIBLE) func _physics_process( delta: float): var h = get_node( "h") h.rotation.y = lerpf( h.rotation.y, kh, h_accel * delta)
Добавка учитывания пустого поиска пересечения..
# player.gd :: move_klik var result = space_state.intersect_ray(query) # узнали пересечение. var r_pos = self.position # добавка кода. if result.is_empty( ): print( "ZERO intersect_ray") else: r_pos = result.position nav_target( r_pos) # приказали _навигатору найти путь. print( r_pos) # Путь, примерно, к этой точке.
// ---
Увлёкся гаражным декором. (изолента на руле ...)
Пытаюсь усвоить и адаптировать чужые заготовки полигонов.
У одного набора полигонов, зажатый цвет-материал.
Я не стал разбираться - сделал две добавочных папки,
где заменил все текстуры на тёмную, и на золотисто-блёклую.
В архиве, эти дубляжы полигонов - хорошо ужымаются - сойдёт подход.
Обычная локация - маленькая, как зал-комната,
чтобы авто-оптимизация была.
Загружаем локацию - некоторые яшики бросают жребий,
чтобы сменить визуалку - некое разны-образие, и работа
заборчиками, и стенами. Накидать яшиков, везде ...
На скриншоте, перед серым рыцарем - большые яшики, как стена.
// Сумка и ярлыки - не работают.
Забавный шэйдэр мультяшного моря..
https://godotshaders.com/shader/wind-waker-water-no-textures-needed/
extends Node3D # b_box_large_m_1 // сцена для создания стены. # b_box_m_1 // сцена для заборчика. # # Внутри сцен, под этот скрипт, подразумеваю три пары яшиков - каждый # из них, можно поворачивать по оси угрик. @export var rot_deg_half := 0 # 0= не крутить карусель. # 10= выбор градуса поворота, плюс-минус 10. # Стараемся не ставить большых чисел. @export var use_dekor := 0 # 0= random // умолчанка выставит разные визуальные кубики. # 1= выставить пару первых яшиков. # 2= вторая пара # 3= третья пара # else: невидимая колизия - не включаем визуалку яшиков. var m1 = [ null, null, null] var m2 = [ null, null, null] var mr = [ 0, 90, 180, 270] func _ready(): # при загрузке локаций if rot_deg_half < 0: rot_deg_half = 0 if rot_deg_half > 30: rot_deg_half = 30 m1[ 0] = get_node( "b1") m1[ 1] = get_node( "d1") m1[ 2] = get_node( "g1") m2[ 0] = get_node( "b2") m2[ 1] = get_node( "d2") m2[ 2] = get_node( "g2") var p1 = m1[ 0] var p2 = m2[ 0] p1.hide( ) # прячем изначальные яшики. p2.hide( ) if use_dekor == 0: p1 = m1[ g2.random( 0, 2)] # жребий от 0 до 2 p2 = m2[ g2.random( 0, 2)] elif use_dekor == 2: p1 = m1[ 1] p2 = m2[ 1] elif use_dekor == 3: p1 = m1[ 2] p2 = m2[ 2] show_rot_box( p1, rot_deg_half) show_rot_box( p2, rot_deg_half) #--------- func show_rot_box( a, do_r: int = 0): if do_r: var rot = mr[ g2.random( 0, 3)] if rot: # нуль оставляем без перемен, а остальное - шанс изменить. if g2.random( 0, 2): if g2.random( 0, 1): rot -= g2.random( 1, do_r) else: rot += g2.random( 1, do_r) #print( "box_rot: ", rot) a.rotation.y = deg_to_rad( rot) # if use_dekor < 4: a.show( ) #---------
slatazan
> Забавный шэйдэр мультяшного моря..
Видел.
Мне он не очень. Я свой написал, сразу с плавучестью.
Стартовая локация, вид сверху.
Набор локаций "монастырь", шаманский - на острове мастеров.
Коридор, сверху - это путь в сад стихий, если экипированы какие-нибудь _крылья.
// - - -
Использую для создания плавных дуг. На картинке - справа.
@tool #n3d_scn_klon.gd extends Node3D @export var init: bool = false: set(x): if x: re_init( ) # init остаётся пустой галочкой. @export var klv: int = 0 # количество клонов. # orig_child_begin @export var child_beg: int = 0 # первый чилд-оригинал, начало жребия. # Чилды-номера, для жребия, и для копирования - обязаны следовать # друг за другом, впритык. Учитывая, что рабочая нода обязана # получить "Make Local", можно передвинуть любого чилда # вышэ-нижэ, в дереве сцены. // Ctrl + up or dn. @export var child_end: int = 0 # заключительный чилд-оригинал, для жребия. # Изначальный нуль, как игнор этого значения, в пользу повтора child_beg. # Однако, выставив негатив, мы указываем количество пустых мест, когда # вместо "второй" штамповки первого чилда, покидаем функцию - пустое место, # чтобы сделать подобный круг фигур, чуть повернуть его, карусельно, # и получить чередование двух клонов ( 1, 2, 1, 2). # # Спец-пробник.. # node1.klv = 7 # node1.child_beg = 0 # m1 # node1.child_end = -1 # node2.klv = 7 # node2.child_beg = 1 # m2 # node2.child_end = -3 # node2.position.x += 1 // сдвинем, и будет: m1 m2 m1 _ m1 m2 m1 @export var auto_rotate: int = 0 # доворачивать клоны, по дуге. @export var after_angle_y: float = 0.0 # карусель-поворот, после каждого клона. @export var after_move: Vector3 = Vector3( 1.0, 0.0, 0.0) # сдвиг, после каждого клона. @export var base_rotation: Vector3 = Vector3( 0.0, 0.0, 0.0) var priv_skip := 0 # сколько пустых мест ужэ сделано, если они нужны. var priv_ang := 0.0 # плавное добавление after_angle_y. var priv_pos := Vector3.ZERO var multi = null # g2.random( 0, 2) нельзя использовать в тул-скрипте @tool func random( min_i: int = 1, max_i: int = 6): if max_i <= min_i: return min_i return ( randi( ) % ( max_i - min_i + 1) ) + min_i #--------- func re_init( ): if klv < 1: print( "klv < 1") return multi = get_node_or_null( "multi") if not multi: #print( "not multi") #return var n3 = Node3D.new( ) if not n3: print( "not NEW multi") return n3.name = "multi" self.add_child( n3) n3.owner = get_tree( ).edited_scene_root multi = get_node_or_null( "multi") # # for a in range( multi.get_child_count( ) -1, -1, -1): # multi.get_child( a).free( ) for a in range( 0, multi.get_child_count( )): #print( "multi clear %d" % a) multi.get_child( a).queue_free( ) priv_ang = 0.0 priv_pos = Vector3.ZERO # --- for i in range( 0, klv): #print( "(klv %d) re_init %d" % [ klv, i] ) if child_beg < 0: child_beg = 0 var x = get_child( random( child_beg, child_end) ) dub_an1( x, base_rotation, after_angle_y, after_move) #for #await get_tree( ).create_timer( 0.1).timeout // микро-пауза #--------- func dub_an1( orig, set_rot: Vector3, aft_ug: float, aft_move: Vector3): var skip := 0 if child_end < 0: if not priv_skip: # будем делать клон. priv_skip = -child_end # назначаем колво будуших пропусков. else: skip = 1 # не клонируем priv_skip -= 1 # и пытаемся обнулить priv_skip # var n = null if not skip: #print( "orig name: %s" % orig.name) n = orig.duplicate( ) # duplicate( flags: int = 15) returning a new node if not n: print( "err duplicate") return multi.add_child( n) n.owner = get_tree( ).edited_scene_root # чтобы нода появилась в окне редактора. n.show( ) n.position = priv_pos #print( "duplicate name: %s" % n.name) else: pass # priv_ang += aft_ug # upd for next var d if aft_ug != 0: d = aft_move.rotated( Vector3( 0,1,0), deg_to_rad( priv_ang)) else: #print( "zero priv_ang") d = aft_move # priv_pos += d if not skip: n.rotation.x = deg_to_rad( set_rot.x) n.rotation.y = deg_to_rad( set_rot.y) n.rotation.z = deg_to_rad( set_rot.z) if auto_rotate: n.rotation.y += deg_to_rad( priv_ang - ( aft_ug * 0.5) ) #---------
Чтобы пере-заход в локацию, ставил кусты на новые места ...
extends Area3D # area_forest.gd # обязательный нулевой чилд - колижн-шэйп "box". # Другие чилды - элементы, спрятаные _под_землю, # чтобы их копировать, и ставить на землю. # Сохраняем эту заготовку, а когда загружаем её, # то правый клик - строка меню "Editable Children". # Габариты колиз-бокса, как область заполнения. # Колиз-бокс должн иметь галочку local_to_scene. # Если одна локация, и много подобных лесных зон, # то нужно применять Make Uniq на ресурсе BoxShape. @export var trig_surprize: int = 0 # Прибавка шанса появления мобов-нежданчиков. var box_size_x: float = 0.0 var box_size_z: float = 0.0 @export var q_x: int = 0 # оставив меньшэ трёх - прерываем # _создание_леса, чтобы, например, использовать зону для логики. var step_x := 0.0 @export var q_z: int = 0 # нужное количество элементов, по оси зэд. var step_z := 0.0 @export var step_shake: float = 0.5 # жребий сдвига хикс-зэд. @export var scale_max: int = 0 # 7, как 0.7 увеличения для элементов. @export var skip_rand: bool = true # оставить-ли пустыми некоторые ячейки грида (q_x * q_z). @export var skip_mid: int = 0 # 0, 1, 2, 3. // Пустота в середине - максимум, три на три. @export var random_dent: float = 0.5 # вдавливание, от жребия. var xh := 0 var zh := 0 var priv_ang := 0.0 # плавное добавление after_angle_y. var priv_pos := Vector3.ZERO var priv_ofs := Vector3.ZERO var priv_use_y05 := 0 var srx := 0 # шаг_радиус для хикса var srz := 0 var bg = null func _ready(): bg = get_node_or_null( "box") if not bg: return box_size_x = bg.shape.size.x box_size_z = bg.shape.size.z call_deferred( "forest2") # на всякий случий, в конце кадра #--------- func forest2(): var elem_q := 0 elem_q = get_child_count() - 1 # минус нулевая коробка. if elem_q < 1: return # не заготовили шаблонов (валунов, кустов). #else: копируем чилда, забирая его, через get_child[ from1] if q_x < 3 or q_z < 3: print( "area_forest ERR( q_x < 3 or q_z < 3)") return step_x = box_size_x / float( q_x +1) # лишний, для зазора. step_z = box_size_z / float( q_z +1) if step_x < 1.0 or step_z < 1.0: return if skip_mid < 0: skip_mid = 0 if skip_mid > 3: skip_mid = 3 if skip_mid: xh = int( (q_x +1) * 0.5) zh = int( (q_z +1) * 0.5) if xh < 2: # игнорим такой пропуск. xh = 0 if zh < 2: zh = 0 if step_shake < 0.0: step_shake = 0.0 if step_shake > 1.0: step_shake = 1.0 srx = int( step_x * step_shake) srz = int( step_z * step_shake) # --- квадратно-гнездовое.. var skip1 := 0 var elem = null var box_half_x = box_size_x * 0.5 var box_half_z = box_size_z * 0.5 for z in range( 0, q_z +1): # плюс пустышка, на нуле if z == 0: continue priv_ang = 0.0 priv_pos = Vector3( -box_half_x + step_x, 0, z * step_z - box_half_z) # Сразу ставим priv_pos.x учитывая пустой шаг. for x in range( 0, q_x +1): # плюс пустышка, на нуле. if x == 0: continue priv_ofs = priv_ofs_rand( 1) skip1 = get_skip( x, z) elem = get_child( g2.random( 1, elem_q)) dub_an1( elem, Vector3( 0, g2.random( 1, 24) * 15, 0), 0.0, Vector3( step_x, 0, 0), skip1 ) # for x # for z #--------- func priv_ofs_rand( use_y: int = 0) -> Vector3: priv_use_y05 = 0 var v: Vector3 = Vector3.ZERO var ox := 0 var oz := 0 if srx: ox = g2.random( 0, srx) if ox: if g2.random( 0, 1): ox = -ox if srz: oz = g2.random( 0, srz) if oz: if g2.random( 0, 1): oz = -oz if ox or oz: v = Vector3( ox, 0, oz) if use_y: if g2.random( 0, 1): # уместно менять угрик после приземления. priv_use_y05 = 1 # return v #--------- func get_skip( x: int, z: int) -> int: if skip_rand: if g2.random( 0, 1): return 1 # не важно, что дальшэ - жребий ужэ порешал. # срезаем углы.. if x == 1 and z == 1: return 1 elif x == q_x and z == 1: return 1 elif x == 1 and z == q_z: return 1 elif x == q_x and z == q_z: return 1 # --- Можно проверить проекцию к габаритам важных нод. # И вернуть единицу, если залезли в габариты домика. var skip1 := 0 if skip_mid < 1: return skip1 if xh < 2 or zh < 2: return skip1 if skip_mid == 1: # примерно средния клетка - пустая. if x == xh and z == zh: skip1 = 1 elif skip_mid == 2: # 2х2 средние клетки. if (x == xh or x == xh + 1) and (z == zh or z == zh + 1): skip1 = 1 elif skip_mid == 3: # 3х3, в середине. if x == xh or x == xh + 1 or x == xh - 1: if z == zh or z == zh + 1 or z == zh - 1: skip1 = 1 return skip1 #--------- func dub_an1( orig, set_rot: Vector3, aft_ug: float, aft_move: Vector3, skip: int = 0) -> void: var n = null if not skip: #print( "orig name: %s" % orig.name) n = orig.duplicate() # duplicate(flags: int = 15) returning a new node if not n: print( "err duplicate") return bg.add_child( n) #n.owner = get_tree().edited_scene_root # чтобы нода появилась в окне редактора. n.show() n.position = priv_pos #print( "duplicate name: %s" % n.name) n.position += priv_ofs if 1: n.global_position = find_floor( n.global_position) if priv_use_y05: n.position.y -= random_dent # чуть провалим else: pass # priv_ang += aft_ug # upd for next var d if aft_ug != 0: d = aft_move.rotated( Vector3( 0,1,0), deg_to_rad( priv_ang)) else: d = aft_move # priv_pos += d if not skip: n.rotation.x = deg_to_rad( set_rot.x) n.rotation.y = deg_to_rad( set_rot.y) n.rotation.z = deg_to_rad( set_rot.z) if 1: #auto_rotate: n.rotation.y += deg_to_rad( priv_ang - (aft_ug * 0.5) ) if scale_max > 0: var sc = float( g2.random( 0, scale_max)) * 0.1 + 1.0 n.scale.x = sc n.scale.y = sc n.scale.z = sc #--------- func find_floor( v: Vector3) -> Vector3: var space_state = get_world_3d().direct_space_state var end = v end.y -= 500.0 # луч вниз var query = PhysicsRayQueryParameters3D.create( v, end) query.collide_with_areas = false var result = space_state.intersect_ray( query) if result.is_empty(): return v return result.position #--------- #.
// --- сумка, травничество, тикалка
Игра стартует. Открываем сумку. Кликаем морковь несколько раз,
и счётчик становится нормальным: 99, вместо "9+".
Потому-что изначально, из кода, добавлен стак в 101 морковь.
Клики по предметам - тэстово отнимают единицу стака.
В локацию, выставлена нода _собираемый_куст - над ним кружочек.
Пробывал три-мерную надпись, вместо круга, но не понравилось.
Собираем куст - фиалка добавилась. // это отмечают в чате.
Персонаж устроен.. отдельный нодо-скрипт, внутри которого,
создан экзэмпляр rpg_u, как обше-деталь для игроков и монстров.
Жмём тэстовую кнопку _удара. Идёт анимация удара, и вдобавок,
пытаемся повесить на нутри-юнит персонажа - огне-тикалку..
player.gd::
unit.eff_set__tik( g1.r_ef_tik_flame, 10, 200, 1)
В индикаторе, видно, как считают секунды огне-тика (10 сек).
Но пока, нет оформления ранений.
При этом тэсте, я заметил, что функция "_physics_process",
у меня, иногда, вызывается по два раза, за один кадр.
# Лично мне пофигу на такое - не собираюсь вникать. # Значит, всю "одно_кадровую" логику надо держать в _process, # которая не связана с колизиями, и я верю - она один раз, в кадр.
// Переход в каркас шаговой игры.. Меню, Арсенал, кнопка СПЕЦ ИГРА.