Character states, aiming and stuff
This commit is contained in:
parent
5b6e4467e9
commit
1079daa49b
@ -97,6 +97,10 @@ std::shared_ptr<const assets::Animation> assets::Animation::LoadFromFile(const s
|
|||||||
{
|
{
|
||||||
iss >> anim->tps_;
|
iss >> anim->tps_;
|
||||||
}
|
}
|
||||||
|
else if (command == "cyclic")
|
||||||
|
{
|
||||||
|
anim->cyclic_ = true;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (anim->channels_.empty())
|
if (anim->channels_.empty())
|
||||||
@ -120,5 +124,9 @@ std::shared_ptr<const assets::Animation> assets::Animation::LoadFromFile(const s
|
|||||||
channel.frames = &anim->frame_refs_[i * anim->num_frames_];
|
channel.frames = &anim->frame_refs_[i * anim->num_frames_];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calc duration
|
||||||
|
auto frame_range = anim->cyclic_ ? anim->num_frames_ : anim->num_frames_ - 1;
|
||||||
|
anim->duration_ = static_cast<float>(frame_range) / anim->tps_;
|
||||||
|
|
||||||
return anim;
|
return anim;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,8 @@ public:
|
|||||||
|
|
||||||
size_t GetNumFrames() const { return num_frames_; }
|
size_t GetNumFrames() const { return num_frames_; }
|
||||||
float GetTPS() const { return tps_; }
|
float GetTPS() const { return tps_; }
|
||||||
float GetDuration() const { return static_cast<float>(num_frames_) / tps_; }
|
float GetDuration() const { return duration_; }
|
||||||
|
bool IsCyclic() const { return cyclic_; }
|
||||||
|
|
||||||
size_t GetNumChannels() const { return channels_.size(); }
|
size_t GetNumChannels() const { return channels_.size(); }
|
||||||
const AnimationChannel& GetChannel(int index) const { return channels_[index]; }
|
const AnimationChannel& GetChannel(int index) const { return channels_[index]; }
|
||||||
@ -32,6 +33,8 @@ public:
|
|||||||
private:
|
private:
|
||||||
size_t num_frames_ = 0;
|
size_t num_frames_ = 0;
|
||||||
float tps_ = 24.0f;
|
float tps_ = 24.0f;
|
||||||
|
bool cyclic_ = false;
|
||||||
|
float duration_ = 0.0f;
|
||||||
|
|
||||||
std::vector<AnimationChannel> channels_;
|
std::vector<AnimationChannel> channels_;
|
||||||
std::vector<const Transform*> frame_refs_;
|
std::vector<const Transform*> frame_refs_;
|
||||||
|
|||||||
@ -40,6 +40,8 @@ std::shared_ptr<const assets::Skeleton> assets::Skeleton::LoadFromFile(const std
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
skeleton->AddAimBones();
|
||||||
|
|
||||||
return skeleton;
|
return skeleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,3 +97,24 @@ void assets::Skeleton::AddAnimation(const std::string& name, const std::shared_p
|
|||||||
anim_idxs_[name] = anims_.size();
|
anim_idxs_[name] = anims_.size();
|
||||||
anims_.push_back(anim);
|
anims_.push_back(anim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void assets::Skeleton::AddAimBones()
|
||||||
|
{
|
||||||
|
AddAimBone("DEF-spine.002", 0.5f);
|
||||||
|
AddAimBone("MCH-spine.002", 0.5f);
|
||||||
|
AddAimBone("DEF-spine.003", 0.5f);
|
||||||
|
AddAimBone("MCH-spine.003", 0.5f);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void assets::Skeleton::AddAimBone(const std::string& name, float weight)
|
||||||
|
{
|
||||||
|
auto idx = GetBoneIndex(name);
|
||||||
|
if (idx < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AimBone aimbone{};
|
||||||
|
aimbone.idx = idx;
|
||||||
|
aimbone.weight = weight;
|
||||||
|
aim_bones_.emplace_back(aimbone);
|
||||||
|
}
|
||||||
|
|||||||
@ -22,6 +22,12 @@ struct Bone
|
|||||||
using AnimIdx = uint8_t;
|
using AnimIdx = uint8_t;
|
||||||
constexpr AnimIdx NO_ANIM = 255;
|
constexpr AnimIdx NO_ANIM = 255;
|
||||||
|
|
||||||
|
struct AimBone
|
||||||
|
{
|
||||||
|
size_t idx;
|
||||||
|
float weight;
|
||||||
|
};
|
||||||
|
|
||||||
class Skeleton
|
class Skeleton
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -37,16 +43,24 @@ public:
|
|||||||
const Animation* GetAnimation(AnimIdx idx) const;
|
const Animation* GetAnimation(AnimIdx idx) const;
|
||||||
const Animation* GetAnimation(const std::string& name) const;
|
const Animation* GetAnimation(const std::string& name) const;
|
||||||
|
|
||||||
|
const std::vector<AimBone> GetAimBones() const { return aim_bones_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void AddBone(const std::string& name, const std::string& parent_name, const Transform& transform);
|
void AddBone(const std::string& name, const std::string& parent_name, const Transform& transform);
|
||||||
void AddAnimation(const std::string& name, const std::shared_ptr<const Animation>& anim);
|
void AddAnimation(const std::string& name, const std::shared_ptr<const Animation>& anim);
|
||||||
|
|
||||||
|
void AddAimBones();
|
||||||
|
void AddAimBone(const std::string& name, float weight);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::string name_;
|
||||||
std::vector<Bone> bones_;
|
std::vector<Bone> bones_;
|
||||||
std::map<std::string, int> bone_map_;
|
std::map<std::string, int> bone_map_;
|
||||||
|
|
||||||
std::vector<std::shared_ptr<const Animation>> anims_;
|
std::vector<std::shared_ptr<const Animation>> anims_;
|
||||||
std::map<std::string, AnimIdx> anim_idxs_;
|
std::map<std::string, AnimIdx> anim_idxs_;
|
||||||
|
|
||||||
|
std::vector<AimBone> aim_bones_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace assets
|
} // namespace assets
|
||||||
@ -196,6 +196,20 @@ static void PollEvents()
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
|
case SDL_MOUSEBUTTONUP:
|
||||||
|
{
|
||||||
|
if (event.button.button == SDL_BUTTON_LEFT)
|
||||||
|
{
|
||||||
|
s_app->Input(game::IN_ATTACK_PRIMARY, event.button.state == SDL_PRESSED, event.button.clicks > 1);
|
||||||
|
}
|
||||||
|
else if (event.button.button == SDL_BUTTON_RIGHT)
|
||||||
|
{
|
||||||
|
s_app->Input(game::IN_ATTACK_SECONDARY, event.button.state == SDL_PRESSED, event.button.clicks > 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -362,16 +376,6 @@ static void Frame()
|
|||||||
int width, height;
|
int width, height;
|
||||||
SDL_GetWindowSize(s_window, &width, &height);
|
SDL_GetWindowSize(s_window, &width, &height);
|
||||||
s_app->SetViewportSize(width, height);
|
s_app->SetViewportSize(width, height);
|
||||||
|
|
||||||
game::PlayerInputFlags input = 0;
|
|
||||||
const uint8_t* kbd_state = SDL_GetKeyboardState(nullptr);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int mouse_state = SDL_GetMouseState(nullptr, nullptr);
|
|
||||||
|
|
||||||
if (mouse_state & SDL_BUTTON(SDL_BUTTON_LEFT))
|
|
||||||
input |= (1 << game::IN_ATTACK);
|
|
||||||
|
|
||||||
s_app->Frame();
|
s_app->Frame();
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ game::Animal::Animal(World& world, const CharacterTuning& tuning, const glm::vec
|
|||||||
SetPosition(position);
|
SetPosition(position);
|
||||||
SetYaw(yaw);
|
SetYaw(yaw);
|
||||||
EnablePhysics(true);
|
EnablePhysics(true);
|
||||||
|
SetMovementType(CMT_TURN);
|
||||||
|
|
||||||
collision::AddObjectFlags(&GetController()->GetBtGhost(), collision::OF_USABLE);
|
collision::AddObjectFlags(&GetController()->GetBtGhost(), collision::OF_USABLE);
|
||||||
}
|
}
|
||||||
@ -40,9 +41,9 @@ void game::Animal::SetRideableInput(PlayerInputFlags in)
|
|||||||
SetInputs(MapPlayerInputToCharacterInput(in));
|
SetInputs(MapPlayerInputToCharacterInput(in));
|
||||||
}
|
}
|
||||||
|
|
||||||
void game::Animal::SetRideableYaw(float yaw)
|
void game::Animal::SetRideableViewAngles(float yaw, float pitch)
|
||||||
{
|
{
|
||||||
SetForwardYaw(yaw);
|
SetViewAngles(yaw, pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game::Animal::OnPassengerChanged(size_t seat_idx, HumanCharacter* passenger)
|
void game::Animal::OnPassengerChanged(size_t seat_idx, HumanCharacter* passenger)
|
||||||
|
|||||||
@ -16,7 +16,7 @@ public:
|
|||||||
virtual void Use(PlayerCharacter& character, uint32_t target_id) override;
|
virtual void Use(PlayerCharacter& character, uint32_t target_id) override;
|
||||||
|
|
||||||
virtual void SetRideableInput(PlayerInputFlags in) override;
|
virtual void SetRideableInput(PlayerInputFlags in) override;
|
||||||
virtual void SetRideableYaw(float yaw) override;
|
virtual void SetRideableViewAngles(float yaw, float pitch) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void OnPassengerChanged(size_t seat_idx, HumanCharacter* passenger) override;
|
virtual void OnPassengerChanged(size_t seat_idx, HumanCharacter* passenger) override;
|
||||||
|
|||||||
@ -38,6 +38,7 @@ void game::Character::Update()
|
|||||||
|
|
||||||
SyncTransformFromController();
|
SyncTransformFromController();
|
||||||
UpdateMovement();
|
UpdateMovement();
|
||||||
|
UpdateActionAnim();
|
||||||
root_.UpdateMatrix();
|
root_.UpdateMatrix();
|
||||||
|
|
||||||
sync_current_ = 1 - sync_current_;
|
sync_current_ = 1 - sync_current_;
|
||||||
@ -100,6 +101,17 @@ void game::Character::SetInput(CharacterInputType type, bool enable)
|
|||||||
in_ &= ~(1 << type);
|
in_ &= ~(1 << type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::Character::SetMovementType(CharacterMovementType type)
|
||||||
|
{
|
||||||
|
movement_ = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::Character::SetViewAngles(float yaw, float pitch)
|
||||||
|
{
|
||||||
|
view_yaw_ = yaw;
|
||||||
|
view_pitch_ = pitch;
|
||||||
|
}
|
||||||
|
|
||||||
void game::Character::SetPosition(const glm::vec3& position)
|
void game::Character::SetPosition(const glm::vec3& position)
|
||||||
{
|
{
|
||||||
root_.local.position = position;
|
root_.local.position = position;
|
||||||
@ -121,6 +133,30 @@ void game::Character::SetRunAnim(const std::string& anim_name)
|
|||||||
animstate_.run_anim_idx = GetAnim(anim_name);
|
animstate_.run_anim_idx = GetAnim(anim_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::Character::PlayActionAnim(assets::AnimIdx anim_idx, float speed)
|
||||||
|
{
|
||||||
|
action_anim_end_ = (anim_idx != assets::NO_ANIM) ? sk_.GetSkeleton()->GetAnimation(anim_idx)->GetDuration() : 0.0f;
|
||||||
|
|
||||||
|
if (animstate_.action_anim_idx != anim_idx)
|
||||||
|
{
|
||||||
|
// continue from current time if same anim
|
||||||
|
animstate_.action_phase = (speed > 0.0f) ? 0.0f : action_anim_end_;
|
||||||
|
}
|
||||||
|
animstate_.action_anim_idx = anim_idx;
|
||||||
|
action_anim_playback_speed_ = speed;
|
||||||
|
action_anim_done_ = anim_idx == assets::NO_ANIM;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::Character::PlayActionAnim(const std::string& anim_name, float speed)
|
||||||
|
{
|
||||||
|
PlayActionAnim(GetAnim(anim_name), speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::Character::ClearActionAnim()
|
||||||
|
{
|
||||||
|
PlayActionAnim(assets::NO_ANIM, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
void game::Character::SyncControllerTransform()
|
void game::Character::SyncControllerTransform()
|
||||||
{
|
{
|
||||||
if (!controller_)
|
if (!controller_)
|
||||||
@ -143,42 +179,58 @@ void game::Character::SyncTransformFromController()
|
|||||||
root_.local.position.z -= z_offset_; // foot pos
|
root_.local.position.z -= z_offset_; // foot pos
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static glm::vec2 GetInputDir(game::CharacterInputFlags in)
|
||||||
|
{
|
||||||
|
glm::vec2 dir(0.0f);
|
||||||
|
|
||||||
|
if (in & (1 << game::CIN_FORWARD))
|
||||||
|
dir.y += 1.0f;
|
||||||
|
|
||||||
|
if (in & (1 << game::CIN_BACKWARD))
|
||||||
|
dir.y -= 1.0f;
|
||||||
|
|
||||||
|
if (in & (1 << game::CIN_RIGHT))
|
||||||
|
dir.x -= 1.0f;
|
||||||
|
|
||||||
|
if (in & (1 << game::CIN_LEFT))
|
||||||
|
dir.x += 1.0f;
|
||||||
|
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
void game::Character::UpdateMovement()
|
void game::Character::UpdateMovement()
|
||||||
{
|
{
|
||||||
|
if (movement_ == CMT_DISABLED)
|
||||||
|
{
|
||||||
|
animstate_.loco_blend = 0.0f;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr float dt = 1.0f / 25.0f;
|
constexpr float dt = 1.0f / 25.0f;
|
||||||
bool walking = false;
|
bool walking = false;
|
||||||
bool running = false;
|
bool running = false;
|
||||||
glm::vec2 movedir(0.0f);
|
|
||||||
|
|
||||||
if (in_ & (1 << CIN_FORWARD))
|
glm::vec3 move_dir(0.0f);
|
||||||
movedir.y += 1.0f;
|
|
||||||
|
|
||||||
if (in_ & (1 << CIN_BACKWARD))
|
auto input_dir = GetInputDir(in_);
|
||||||
movedir.y -= 1.0f;
|
if (input_dir.x != 0.0f || input_dir.y != 0.0f)
|
||||||
|
|
||||||
if (in_ & (1 << CIN_RIGHT))
|
|
||||||
movedir.x -= 1.0f;
|
|
||||||
|
|
||||||
if (in_ & (1 << CIN_LEFT))
|
|
||||||
movedir.x += 1.0f;
|
|
||||||
|
|
||||||
glm::vec3 walkdir(0.0f);
|
|
||||||
|
|
||||||
if (movedir.x != 0.0f || movedir.y != 0.0f)
|
|
||||||
{
|
{
|
||||||
walking = true;
|
walking = true;
|
||||||
|
|
||||||
if (in_ & (1 << CIN_SPRINT))
|
if (in_ & (1 << CIN_SPRINT))
|
||||||
running = true;
|
running = true;
|
||||||
|
|
||||||
float target_yaw = forward_yaw_ + std::atan2(movedir.x, movedir.y);
|
const bool directional = (movement_ == CMT_DIRECTIONAL);
|
||||||
Turn(yaw_, target_yaw, turn_speed_ * dt);
|
|
||||||
|
|
||||||
glm::vec3 forward_dir(-glm::sin(yaw_), glm::cos(yaw_), 0.0f);
|
|
||||||
walkdir = forward_dir * walk_speed_ * dt;
|
|
||||||
|
|
||||||
|
float relative_yaw = std::atan2(input_dir.x, input_dir.y);
|
||||||
|
float turn_yaw = directional ? view_yaw_ : view_yaw_ + relative_yaw;
|
||||||
|
Turn(yaw_, turn_yaw, turn_speed_ * dt);
|
||||||
|
float move_yaw = directional ? yaw_ + relative_yaw : yaw_;
|
||||||
|
|
||||||
|
move_dir = glm::vec3(-glm::sin(move_yaw), glm::cos(move_yaw), 0.0f) * walk_speed_ * dt;
|
||||||
|
|
||||||
if (running)
|
if (running)
|
||||||
walkdir *= run_speed_mult_;
|
move_dir *= run_speed_mult_;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +239,7 @@ void game::Character::UpdateMovement()
|
|||||||
if (controller_)
|
if (controller_)
|
||||||
{
|
{
|
||||||
auto& bt_character = controller_->GetBtController();
|
auto& bt_character = controller_->GetBtController();
|
||||||
bt_character.setWalkDirection(btVector3(walkdir.x, walkdir.y, walkdir.z));
|
bt_character.setWalkDirection(btVector3(move_dir.x, move_dir.y, move_dir.z));
|
||||||
|
|
||||||
if (in_ & (1 << CIN_JUMP) && bt_character.canJump())
|
if (in_ & (1 << CIN_JUMP) && bt_character.canJump())
|
||||||
{
|
{
|
||||||
@ -202,6 +254,9 @@ void game::Character::UpdateMovement()
|
|||||||
if (running)
|
if (running)
|
||||||
anim_speed *= run_speed_mult_;
|
anim_speed *= run_speed_mult_;
|
||||||
animstate_.loco_phase = glm::mod(animstate_.loco_phase + anim_speed * dt, 1.0f);
|
animstate_.loco_phase = glm::mod(animstate_.loco_phase + anim_speed * dt, 1.0f);
|
||||||
|
|
||||||
|
animstate_.pitch = view_pitch_;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game::Character::UpdateSyncState()
|
void game::Character::UpdateSyncState()
|
||||||
@ -220,6 +275,14 @@ void game::Character::UpdateSyncState()
|
|||||||
state.run_anim = animstate_.run_anim_idx;
|
state.run_anim = animstate_.run_anim_idx;
|
||||||
state.loco_phase.Encode(animstate_.loco_phase);
|
state.loco_phase.Encode(animstate_.loco_phase);
|
||||||
state.loco_blend.Encode(animstate_.loco_blend);
|
state.loco_blend.Encode(animstate_.loco_blend);
|
||||||
|
|
||||||
|
// action
|
||||||
|
state.action_anim = animstate_.action_anim_idx;
|
||||||
|
state.action_phase.Encode(animstate_.action_phase);
|
||||||
|
|
||||||
|
// aim
|
||||||
|
state.aim_yaw.Encode(animstate_.yaw);
|
||||||
|
state.aim_pitch.Encode(animstate_.pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game::Character::SendUpdateMsg()
|
void game::Character::SendUpdateMsg()
|
||||||
@ -282,6 +345,31 @@ game::CharacterSyncFieldFlags game::Character::WriteState(net::OutMessage& msg,
|
|||||||
net::WriteDelta(msg, curr.loco_phase, base.loco_phase);
|
net::WriteDelta(msg, curr.loco_phase, base.loco_phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// action anim
|
||||||
|
if (curr.action_anim != base.action_anim)
|
||||||
|
{
|
||||||
|
fields |= CSF_ACTION_ANIM;
|
||||||
|
|
||||||
|
msg.Write(curr.action_anim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// action phase
|
||||||
|
if (curr.action_phase.value != base.action_phase.value)
|
||||||
|
{
|
||||||
|
fields |= CSF_ACTION_PHASE;
|
||||||
|
|
||||||
|
net::WriteDelta(msg, curr.action_phase, base.action_phase);
|
||||||
|
}
|
||||||
|
|
||||||
|
// aim
|
||||||
|
if (curr.aim_yaw.value != base.aim_yaw.value || curr.aim_pitch.value != base.aim_pitch.value)
|
||||||
|
{
|
||||||
|
fields |= CSF_AIM;
|
||||||
|
|
||||||
|
net::WriteDelta(msg, curr.aim_yaw.value, base.aim_yaw.value);
|
||||||
|
net::WriteDelta(msg, curr.aim_pitch.value, base.aim_pitch.value);
|
||||||
|
}
|
||||||
|
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,6 +378,31 @@ assets::AnimIdx game::Character::GetAnim(const std::string& name) const
|
|||||||
return sk_.GetSkeleton()->GetAnimationIdx(name);
|
return sk_.GetSkeleton()->GetAnimationIdx(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::Character::UpdateActionAnim()
|
||||||
|
{
|
||||||
|
if (action_anim_done_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
animstate_.action_phase += action_anim_playback_speed_ * (1.0f / 25.0f);
|
||||||
|
|
||||||
|
if (action_anim_playback_speed_ > 0.0f)
|
||||||
|
{
|
||||||
|
if (animstate_.action_phase >= action_anim_end_)
|
||||||
|
{
|
||||||
|
animstate_.action_phase = action_anim_end_;
|
||||||
|
action_anim_done_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (animstate_.action_phase <= 0.0f)
|
||||||
|
{
|
||||||
|
animstate_.action_phase = 0.0f;
|
||||||
|
action_anim_done_ = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
game::CharacterPhysicsController::CharacterPhysicsController(Character& character, btDynamicsWorld& bt_world, btCapsuleShapeZ& bt_shape)
|
game::CharacterPhysicsController::CharacterPhysicsController(Character& character, btDynamicsWorld& bt_world, btCapsuleShapeZ& bt_shape)
|
||||||
: character_(character), bt_world_(bt_world), bt_character_(&bt_ghost_, &bt_shape, 0.3f, btVector3(0, 0, 1))
|
: character_(character), bt_world_(bt_world), bt_character_(&bt_ghost_, &bt_shape, 0.3f, btVector3(0, 0, 1))
|
||||||
{
|
{
|
||||||
|
|||||||
@ -45,6 +45,13 @@ private:
|
|||||||
btKinematicCharacterController bt_character_;
|
btKinematicCharacterController bt_character_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum CharacterMovementType
|
||||||
|
{
|
||||||
|
CMT_DISABLED,
|
||||||
|
CMT_TURN,
|
||||||
|
CMT_DIRECTIONAL,
|
||||||
|
};
|
||||||
|
|
||||||
class Character : public Entity
|
class Character : public Entity
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -64,13 +71,17 @@ public:
|
|||||||
|
|
||||||
void SetInput(CharacterInputType type, bool enable);
|
void SetInput(CharacterInputType type, bool enable);
|
||||||
void SetInputs(CharacterInputFlags inputs) { in_ = inputs; }
|
void SetInputs(CharacterInputFlags inputs) { in_ = inputs; }
|
||||||
|
CharacterInputFlags GetInputs() const { return in_; }
|
||||||
|
|
||||||
|
void SetMovementType(CharacterMovementType type);
|
||||||
|
|
||||||
|
void SetViewAngles(float yaw, float pitch);
|
||||||
|
float GetViewYaw() const { return view_yaw_; }
|
||||||
|
float GetViewPitch() const { return view_pitch_; }
|
||||||
|
|
||||||
void SetForwardYaw(float yaw) { forward_yaw_ = yaw; }
|
|
||||||
float GetForwardYaw() const { return forward_yaw_; }
|
|
||||||
void SetYaw(float yaw) { yaw_ = yaw; }
|
void SetYaw(float yaw) { yaw_ = yaw; }
|
||||||
|
|
||||||
void SetPosition(const glm::vec3& position);
|
void SetPosition(const glm::vec3& position);
|
||||||
|
|
||||||
|
|
||||||
~Character() override = default;
|
~Character() override = default;
|
||||||
|
|
||||||
@ -78,6 +89,10 @@ protected:
|
|||||||
void SetIdleAnim(const std::string& anim_name);
|
void SetIdleAnim(const std::string& anim_name);
|
||||||
void SetWalkAnim(const std::string& anim_name);
|
void SetWalkAnim(const std::string& anim_name);
|
||||||
void SetRunAnim(const std::string& anim_name);
|
void SetRunAnim(const std::string& anim_name);
|
||||||
|
void PlayActionAnim(assets::AnimIdx anim_idx, float speed);
|
||||||
|
void PlayActionAnim(const std::string& anim_name, float speed = 1.0f);
|
||||||
|
void ClearActionAnim();
|
||||||
|
bool IsActionAnimDone() { return action_anim_done_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SyncControllerTransform();
|
void SyncControllerTransform();
|
||||||
@ -90,6 +105,8 @@ private:
|
|||||||
|
|
||||||
assets::AnimIdx GetAnim(const std::string& name) const;
|
assets::AnimIdx GetAnim(const std::string& name) const;
|
||||||
|
|
||||||
|
void UpdateActionAnim();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
float turn_speed_ = 8.0f;
|
float turn_speed_ = 8.0f;
|
||||||
float walk_speed_ = 2.0f;
|
float walk_speed_ = 2.0f;
|
||||||
@ -108,13 +125,20 @@ private:
|
|||||||
std::unique_ptr<CharacterPhysicsController> controller_;
|
std::unique_ptr<CharacterPhysicsController> controller_;
|
||||||
|
|
||||||
float yaw_ = 0.0f;
|
float yaw_ = 0.0f;
|
||||||
float forward_yaw_ = 0.0f;
|
float view_yaw_ = 0.0f;
|
||||||
|
float view_pitch_ = 0.0f;
|
||||||
|
|
||||||
SkeletonInstance sk_;
|
SkeletonInstance sk_;
|
||||||
CharacterAnimState animstate_;
|
CharacterAnimState animstate_;
|
||||||
|
|
||||||
CharacterSyncState sync_[2];
|
CharacterSyncState sync_[2];
|
||||||
size_t sync_current_ = 0;
|
size_t sync_current_ = 0;
|
||||||
|
|
||||||
|
CharacterMovementType movement_ = CMT_DISABLED;
|
||||||
|
|
||||||
|
float action_anim_playback_speed_ = 0.0f;
|
||||||
|
float action_anim_end_ = 0.0f;
|
||||||
|
bool action_anim_done_ = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace game
|
} // namespace game
|
||||||
@ -26,7 +26,6 @@ void game::CharacterAnimState::ApplyToSkeleton(SkeletonInstance& sk) const
|
|||||||
sk.ApplySkelAnim(*idle_anim, loco_phase, 1.0f);
|
sk.ApplySkelAnim(*idle_anim, loco_phase, 1.0f);
|
||||||
sk.ApplySkelAnim(*walk_anim, loco_phase, UnMix(0.0f, 0.5f, loco_blend));
|
sk.ApplySkelAnim(*walk_anim, loco_phase, UnMix(0.0f, 0.5f, loco_blend));
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (loco_blend == 0.5f) // walk
|
else if (loco_blend == 0.5f) // walk
|
||||||
{
|
{
|
||||||
sk.ApplySkelAnim(*walk_anim, loco_phase, 1.0f);
|
sk.ApplySkelAnim(*walk_anim, loco_phase, 1.0f);
|
||||||
@ -41,4 +40,16 @@ void game::CharacterAnimState::ApplyToSkeleton(SkeletonInstance& sk) const
|
|||||||
sk.ApplySkelAnim(*run_anim, loco_phase, 1.0f);
|
sk.ApplySkelAnim(*run_anim, loco_phase, 1.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// action
|
||||||
|
auto action_anim = skeleton->GetAnimation(action_anim_idx);
|
||||||
|
if (action_anim)
|
||||||
|
{
|
||||||
|
sk.ApplySkelAnim(*action_anim, action_phase, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glm::abs(yaw) > 0.01f || glm::abs(pitch) > 0.01f)
|
||||||
|
{
|
||||||
|
sk.ApplyAim(yaw, pitch);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,12 +8,18 @@ namespace game
|
|||||||
struct CharacterAnimState
|
struct CharacterAnimState
|
||||||
{
|
{
|
||||||
assets::AnimIdx idle_anim_idx = assets::NO_ANIM;
|
assets::AnimIdx idle_anim_idx = assets::NO_ANIM;
|
||||||
|
|
||||||
assets::AnimIdx walk_anim_idx = assets::NO_ANIM;
|
assets::AnimIdx walk_anim_idx = assets::NO_ANIM;
|
||||||
assets::AnimIdx run_anim_idx = assets::NO_ANIM;
|
assets::AnimIdx run_anim_idx = assets::NO_ANIM;
|
||||||
|
|
||||||
float loco_blend = 0.0f;
|
float loco_blend = 0.0f;
|
||||||
float loco_phase = 0.0f;
|
float loco_phase = 0.0f;
|
||||||
|
|
||||||
|
assets::AnimIdx action_anim_idx = assets::NO_ANIM;
|
||||||
|
float action_phase = 0.0f;
|
||||||
|
|
||||||
|
float yaw = 0.0f;
|
||||||
|
float pitch = 0.0f;
|
||||||
|
|
||||||
void ApplyToSkeleton(SkeletonInstance& sk) const;
|
void ApplyToSkeleton(SkeletonInstance& sk) const;
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -22,20 +22,30 @@ struct CharacterSyncState
|
|||||||
assets::AnimIdx run_anim = assets::NO_ANIM;
|
assets::AnimIdx run_anim = assets::NO_ANIM;
|
||||||
net::AnimBlendQ loco_blend;
|
net::AnimBlendQ loco_blend;
|
||||||
net::AnimTimeQ loco_phase;
|
net::AnimTimeQ loco_phase;
|
||||||
|
|
||||||
|
// action anim
|
||||||
|
assets::AnimIdx action_anim = assets::NO_ANIM;
|
||||||
|
net::AnimTimeQ action_phase;
|
||||||
|
|
||||||
|
// aim
|
||||||
|
net::AnimAimAngleQ aim_yaw;
|
||||||
|
net::AnimAimAngleQ aim_pitch;
|
||||||
|
|
||||||
//assets::AnimIdx strafe_left_anim = assets::NO_ANIM;
|
//assets::AnimIdx strafe_left_anim = assets::NO_ANIM;
|
||||||
//assets::AnimIdx strafe_right_anim = assets::NO_ANIM;
|
//assets::AnimIdx strafe_right_anim = assets::NO_ANIM;
|
||||||
|
|
||||||
// TODO: action
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using CharacterSyncFieldFlags = uint8_t;
|
using CharacterSyncFieldFlags = uint8_t;
|
||||||
|
|
||||||
enum CharacterSyncFieldFlag
|
enum CharacterSyncFieldFlag
|
||||||
{
|
{
|
||||||
CSF_TRANSFORM = 0x01,
|
CSF_TRANSFORM = 1,
|
||||||
CSF_IDLE_ANIM = 0x02,
|
CSF_IDLE_ANIM = 2,
|
||||||
CSF_LOCO_ANIMS = 0x04,
|
CSF_LOCO_ANIMS = 4,
|
||||||
CSF_LOCO_VALS = 0x08,
|
CSF_LOCO_VALS = 8,
|
||||||
|
CSF_ACTION_ANIM = 16,
|
||||||
|
CSF_ACTION_PHASE = 32,
|
||||||
|
CSF_AIM = 64,
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace game
|
} // namespace game
|
||||||
@ -45,7 +45,7 @@ void game::EnterableWorld::PlayerViewAnglesChanged(Player& player, float yaw, fl
|
|||||||
|
|
||||||
auto character = it->second;
|
auto character = it->second;
|
||||||
|
|
||||||
character->SetForwardYaw(yaw);
|
character->SetViewAngles(yaw, pitch);
|
||||||
}
|
}
|
||||||
|
|
||||||
void game::EnterableWorld::RemovePlayer(Player& player)
|
void game::EnterableWorld::RemovePlayer(Player& player)
|
||||||
|
|||||||
@ -17,33 +17,27 @@ game::HumanCharacter::HumanCharacter(World& world, const HumanCharacterTuning& t
|
|||||||
SetWalkAnim("walk");
|
SetWalkAnim("walk");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::HumanCharacter::Update()
|
||||||
|
{
|
||||||
|
UpdateState();
|
||||||
|
UpdateActionState();
|
||||||
|
Super::Update();
|
||||||
|
}
|
||||||
|
|
||||||
void game::HumanCharacter::SetRideable(Rideable* rideable, size_t seat_idx)
|
void game::HumanCharacter::SetRideable(Rideable* rideable, size_t seat_idx)
|
||||||
{
|
{
|
||||||
if (rideable == rideable_ && seat_idx == seat_idx_)
|
if (rideable == rideable_ && seat_idx == seat_idx_)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (rideable)
|
if (rideable_)
|
||||||
{
|
{
|
||||||
SetPosition(rideable->GetSeatOffset(seat_idx));
|
|
||||||
EnablePhysics(false);
|
|
||||||
|
|
||||||
Attach(rideable->GetEntity().GetEntNum());
|
|
||||||
SetIdleAnim((rideable->GetRideableType() == RIDEABLE_VEHICLE && seat_idx == 0) ? "vehicle_drive" : "vehicle_passenger");
|
|
||||||
SetYaw(0.0f);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EnablePhysics(true);
|
|
||||||
|
|
||||||
glm::vec3 seat_loc = rideable_->GetSeatOffset(seat_idx_);
|
glm::vec3 seat_loc = rideable_->GetSeatOffset(seat_idx_);
|
||||||
seat_loc.x += glm::sign(seat_loc.x) * 0.5f; // to the side
|
seat_loc.x += glm::sign(seat_loc.x) * 0.5f; // to the side
|
||||||
|
|
||||||
glm::vec3 pos = rideable_->GetEntity().GetRoot().matrix * glm::vec4(seat_loc, 1.0f);
|
glm::vec3 pos = rideable_->GetEntity().GetRoot().matrix * glm::vec4(seat_loc, 1.0f);
|
||||||
pos.z += 0.5f;
|
pos.z += 0.5f;
|
||||||
SetPosition(pos);
|
|
||||||
|
|
||||||
Attach(0);
|
rideable_exit_pos_ = pos;
|
||||||
SetIdleAnim("idle");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rideable_ = rideable;
|
rideable_ = rideable;
|
||||||
@ -51,8 +45,10 @@ void game::HumanCharacter::SetRideable(Rideable* rideable, size_t seat_idx)
|
|||||||
seat_idx_ = seat_idx;
|
seat_idx_ = seat_idx;
|
||||||
is_driver_ = rideable && seat_idx_ == 0;
|
is_driver_ = rideable && seat_idx_ == 0;
|
||||||
|
|
||||||
|
SetSignal(HSS_RIDEABLE_CHANGED);
|
||||||
OnRideableChanged();
|
OnRideableChanged();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void game::HumanCharacter::Ride(Rideable* rideable, size_t seat_idx)
|
void game::HumanCharacter::Ride(Rideable* rideable, size_t seat_idx)
|
||||||
@ -72,3 +68,238 @@ game::HumanCharacter::~HumanCharacter()
|
|||||||
{
|
{
|
||||||
Ride(nullptr, 0); // exit rideable
|
Ride(nullptr, 0); // exit rideable
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::HumanCharacter::UpdateState()
|
||||||
|
{
|
||||||
|
struct HumanCharacterStateTableEntry
|
||||||
|
{
|
||||||
|
void (HumanCharacter::*enter)();
|
||||||
|
HumanCharacterState (HumanCharacter::*update)();
|
||||||
|
void (HumanCharacter::*exit)();
|
||||||
|
};
|
||||||
|
|
||||||
|
static const HumanCharacterStateTableEntry state_table[] =
|
||||||
|
{
|
||||||
|
// HS_INIT
|
||||||
|
{
|
||||||
|
nullptr,
|
||||||
|
&HumanCharacter::StateInitUpdate,
|
||||||
|
nullptr,
|
||||||
|
},
|
||||||
|
|
||||||
|
// HS_ON_FOOT
|
||||||
|
{
|
||||||
|
&HumanCharacter::StateOnFootEnter,
|
||||||
|
&HumanCharacter::StateOnFootUpdate,
|
||||||
|
nullptr,
|
||||||
|
},
|
||||||
|
|
||||||
|
// HS_RIDING
|
||||||
|
{
|
||||||
|
&HumanCharacter::StateRidingEnter,
|
||||||
|
&HumanCharacter::StateRidingUpdate,
|
||||||
|
&HumanCharacter::StateRidingExit,
|
||||||
|
},
|
||||||
|
|
||||||
|
// HS_KNOCKED_DOWN
|
||||||
|
{
|
||||||
|
nullptr,
|
||||||
|
&HumanCharacter::StateKnockedDownUpdate,
|
||||||
|
nullptr,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto new_state = (this->*state_table[state_].update)();
|
||||||
|
|
||||||
|
if (new_state == state_)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (auto exit_fun = state_table[state_].exit)
|
||||||
|
(this->*exit_fun)();
|
||||||
|
|
||||||
|
state_ = new_state;
|
||||||
|
|
||||||
|
if (auto enter_fun = state_table[state_].enter)
|
||||||
|
(this->*enter_fun)();
|
||||||
|
}
|
||||||
|
|
||||||
|
signals_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::HumanCharacter::SetSignal(HumanCharacterStateSignal signal)
|
||||||
|
{
|
||||||
|
signals_ |= signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool game::HumanCharacter::PopSignal(HumanCharacterStateSignal signal)
|
||||||
|
{
|
||||||
|
if (signals_ & signal)
|
||||||
|
{
|
||||||
|
signals_ &= ~signal;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::HumanCharacterState game::HumanCharacter::StateInitUpdate()
|
||||||
|
{
|
||||||
|
if (GetRideable())
|
||||||
|
return HS_RIDING;
|
||||||
|
|
||||||
|
return HS_ON_FOOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::HumanCharacter::StateOnFootEnter()
|
||||||
|
{
|
||||||
|
SetIdleAnim("idle");
|
||||||
|
SetWalkAnim("walk");
|
||||||
|
SetMovementType(CMT_TURN);
|
||||||
|
EnablePhysics(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
game::HumanCharacterState game::HumanCharacter::StateOnFootUpdate()
|
||||||
|
{
|
||||||
|
if (PopSignal(HSS_RIDEABLE_CHANGED))
|
||||||
|
return HS_INIT;
|
||||||
|
|
||||||
|
if (PopSignal(HSS_KNOCK_DOWN))
|
||||||
|
return HS_KNOCKED_DOWN;
|
||||||
|
|
||||||
|
SetMovementType(aiming_ ? CMT_DIRECTIONAL : CMT_TURN);
|
||||||
|
|
||||||
|
return HS_ON_FOOT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::HumanCharacter::StateRidingEnter()
|
||||||
|
{
|
||||||
|
auto rideable = GetRideable();
|
||||||
|
SetPosition(rideable->GetSeatOffset(seat_idx_));
|
||||||
|
EnablePhysics(false);
|
||||||
|
|
||||||
|
Attach(rideable->GetEntity().GetEntNum());
|
||||||
|
SetIdleAnim((rideable->GetRideableType() == RIDEABLE_VEHICLE && seat_idx_ == 0) ? "vehicle_drive" : "vehicle_passenger");
|
||||||
|
SetYaw(0.0f);
|
||||||
|
SetMovementType(CMT_DISABLED);
|
||||||
|
}
|
||||||
|
|
||||||
|
game::HumanCharacterState game::HumanCharacter::StateRidingUpdate()
|
||||||
|
{
|
||||||
|
if (!GetRideable())
|
||||||
|
return HS_INIT;
|
||||||
|
|
||||||
|
if (PopSignal(HSS_RIDEABLE_CHANGED))
|
||||||
|
return HS_INIT;
|
||||||
|
|
||||||
|
return HS_RIDING;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::HumanCharacter::StateRidingExit()
|
||||||
|
{
|
||||||
|
EnablePhysics(true);
|
||||||
|
SetPosition(rideable_exit_pos_);
|
||||||
|
Attach(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
game::HumanCharacterState game::HumanCharacter::StateKnockedDownUpdate()
|
||||||
|
{
|
||||||
|
return HS_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::HumanCharacter::UpdateActionState()
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto new_state = CheckActionStateTransition();
|
||||||
|
|
||||||
|
if (new_state == actionstate_)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ExitActionState();
|
||||||
|
actionstate_ = new_state;
|
||||||
|
EnterActionState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::HumanCharacter::EnterActionState()
|
||||||
|
{
|
||||||
|
switch (actionstate_)
|
||||||
|
{
|
||||||
|
case ACTION_IDLE:
|
||||||
|
ClearActionAnim();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ACTION_AIM:
|
||||||
|
PlayActionAnim("rifle_aim", 3.0f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ACTION_AIMING:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ACTION_FIRE:
|
||||||
|
PlayActionAnim("rifle_fire");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ACTION_UNAIM:
|
||||||
|
PlayActionAnim("rifle_aim", -3.0f);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
game::ActionState game::HumanCharacter::CheckActionStateTransition()
|
||||||
|
{
|
||||||
|
switch (actionstate_)
|
||||||
|
{
|
||||||
|
case ACTION_IDLE:
|
||||||
|
if (aiming_) // want aim
|
||||||
|
return ACTION_AIM;
|
||||||
|
|
||||||
|
return ACTION_IDLE;
|
||||||
|
|
||||||
|
case ACTION_AIM:
|
||||||
|
if (IsActionAnimDone())
|
||||||
|
return ACTION_AIMING;
|
||||||
|
|
||||||
|
if (!aiming_) // stop aiming immediately
|
||||||
|
return ACTION_UNAIM;
|
||||||
|
|
||||||
|
return ACTION_AIM;
|
||||||
|
|
||||||
|
case ACTION_AIMING:
|
||||||
|
if (!aiming_)
|
||||||
|
return ACTION_UNAIM; // wants aim no more
|
||||||
|
|
||||||
|
// TODO: check fire
|
||||||
|
|
||||||
|
return ACTION_AIMING;
|
||||||
|
|
||||||
|
case ACTION_FIRE:
|
||||||
|
return ACTION_FIRE;
|
||||||
|
|
||||||
|
case ACTION_UNAIM:
|
||||||
|
if (IsActionAnimDone())
|
||||||
|
return ACTION_IDLE;
|
||||||
|
|
||||||
|
if (aiming_) // start aiming again
|
||||||
|
return ACTION_AIM;
|
||||||
|
|
||||||
|
return ACTION_UNAIM;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return actionstate_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void game::HumanCharacter::ExitActionState()
|
||||||
|
{
|
||||||
|
switch (actionstate_)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -13,6 +13,31 @@ struct HumanCharacterTuning
|
|||||||
class Rideable;
|
class Rideable;
|
||||||
class DrivableVehicle;
|
class DrivableVehicle;
|
||||||
|
|
||||||
|
using HumanCharacterStateSignals = uint32_t;
|
||||||
|
|
||||||
|
enum HumanCharacterStateSignal : HumanCharacterStateSignals
|
||||||
|
{
|
||||||
|
HSS_KNOCK_DOWN = 1,
|
||||||
|
HSS_RIDEABLE_CHANGED = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HumanCharacterState
|
||||||
|
{
|
||||||
|
HS_INIT,
|
||||||
|
HS_ON_FOOT,
|
||||||
|
HS_RIDING,
|
||||||
|
HS_KNOCKED_DOWN,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ActionState
|
||||||
|
{
|
||||||
|
ACTION_IDLE,
|
||||||
|
ACTION_AIM,
|
||||||
|
ACTION_AIMING,
|
||||||
|
ACTION_FIRE,
|
||||||
|
ACTION_UNAIM,
|
||||||
|
};
|
||||||
|
|
||||||
class HumanCharacter : public Character
|
class HumanCharacter : public Character
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -20,6 +45,8 @@ public:
|
|||||||
|
|
||||||
HumanCharacter(World& world, const HumanCharacterTuning& tuning);
|
HumanCharacter(World& world, const HumanCharacterTuning& tuning);
|
||||||
|
|
||||||
|
virtual void Update() override;
|
||||||
|
|
||||||
const HumanCharacterTuning& GetHumanTuning() const { return human_tuning_; }
|
const HumanCharacterTuning& GetHumanTuning() const { return human_tuning_; }
|
||||||
|
|
||||||
void SetRideable(Rideable* rideable, size_t seat_idx); // called by Rideable!!
|
void SetRideable(Rideable* rideable, size_t seat_idx); // called by Rideable!!
|
||||||
@ -31,11 +58,41 @@ public:
|
|||||||
size_t GeatSeatIdx() const { return seat_idx_; }
|
size_t GeatSeatIdx() const { return seat_idx_; }
|
||||||
bool IsDriver() const { return is_driver_; }
|
bool IsDriver() const { return is_driver_; }
|
||||||
|
|
||||||
|
void SetAiming(bool aiming) { aiming_ = aiming; }
|
||||||
|
|
||||||
virtual ~HumanCharacter() override;
|
virtual ~HumanCharacter() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void OnRideableChanged() {}
|
virtual void OnRideableChanged() {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void UpdateState();
|
||||||
|
void SetSignal(HumanCharacterStateSignal signal);
|
||||||
|
bool PopSignal(HumanCharacterStateSignal signal);
|
||||||
|
|
||||||
|
//void StateInitEnter();
|
||||||
|
HumanCharacterState StateInitUpdate();
|
||||||
|
//void StateInitExit();
|
||||||
|
|
||||||
|
void StateOnFootEnter();
|
||||||
|
HumanCharacterState StateOnFootUpdate();
|
||||||
|
//void StateOnFootExit();
|
||||||
|
|
||||||
|
void StateRidingEnter();
|
||||||
|
HumanCharacterState StateRidingUpdate();
|
||||||
|
void StateRidingExit();
|
||||||
|
|
||||||
|
// void StateKnockedDownEnter();
|
||||||
|
HumanCharacterState StateKnockedDownUpdate();
|
||||||
|
// void StateKnockedDownExit();
|
||||||
|
|
||||||
|
|
||||||
|
void UpdateActionState();
|
||||||
|
|
||||||
|
void EnterActionState();
|
||||||
|
ActionState CheckActionStateTransition();
|
||||||
|
void ExitActionState();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HumanCharacterTuning human_tuning_;
|
HumanCharacterTuning human_tuning_;
|
||||||
|
|
||||||
@ -43,6 +100,16 @@ private:
|
|||||||
DrivableVehicle* vehicle_ = nullptr;
|
DrivableVehicle* vehicle_ = nullptr;
|
||||||
size_t seat_idx_ = 0;
|
size_t seat_idx_ = 0;
|
||||||
bool is_driver_ = false;
|
bool is_driver_ = false;
|
||||||
|
|
||||||
|
HumanCharacterState state_ = HS_INIT;
|
||||||
|
HumanCharacterStateSignals signals_ = 0;
|
||||||
|
|
||||||
|
glm::vec3 rideable_exit_pos_ = glm::vec3(0.0f);
|
||||||
|
|
||||||
|
bool aiming_ = false;
|
||||||
|
|
||||||
|
ActionState actionstate_ = ACTION_IDLE;
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ void game::PlayerCharacter::Update()
|
|||||||
|
|
||||||
if (GetRideable() && IsDriver())
|
if (GetRideable() && IsDriver())
|
||||||
{
|
{
|
||||||
GetRideable()->SetRideableYaw(GetForwardYaw());
|
GetRideable()->SetRideableViewAngles(GetViewYaw(), GetViewPitch());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +78,8 @@ void game::PlayerCharacter::UpdateInputs()
|
|||||||
{
|
{
|
||||||
SetInputs(MapPlayerInputToCharacterInput(in));
|
SetInputs(MapPlayerInputToCharacterInput(in));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetAiming(in & (1 << IN_ATTACK_SECONDARY));
|
||||||
}
|
}
|
||||||
|
|
||||||
void game::PlayerCharacter::UpdateUseTarget()
|
void game::PlayerCharacter::UpdateUseTarget()
|
||||||
|
|||||||
@ -16,7 +16,8 @@ namespace game
|
|||||||
IN_CROUCH,
|
IN_CROUCH,
|
||||||
IN_SPRINT,
|
IN_SPRINT,
|
||||||
IN_USE,
|
IN_USE,
|
||||||
IN_ATTACK,
|
IN_ATTACK_PRIMARY,
|
||||||
|
IN_ATTACK_SECONDARY,
|
||||||
IN_DEBUG1,
|
IN_DEBUG1,
|
||||||
IN_DEBUG2,
|
IN_DEBUG2,
|
||||||
IN_DEBUG3,
|
IN_DEBUG3,
|
||||||
|
|||||||
@ -31,7 +31,7 @@ public:
|
|||||||
void KickAll();
|
void KickAll();
|
||||||
|
|
||||||
virtual void SetRideableInput(PlayerInputFlags in) {}
|
virtual void SetRideableInput(PlayerInputFlags in) {}
|
||||||
virtual void SetRideableYaw(float yaw) {}
|
virtual void SetRideableViewAngles(float yaw, float pitch) {}
|
||||||
|
|
||||||
RideableType GetRideableType() const { return type_; }
|
RideableType GetRideableType() const { return type_; }
|
||||||
|
|
||||||
|
|||||||
@ -52,6 +52,20 @@ void game::SkeletonInstance::ApplySkelAnim(const assets::Animation& anim, float
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void game::SkeletonInstance::ApplyAim(float yaw, float pitch)
|
||||||
|
{
|
||||||
|
const auto& aim_bones = skeleton_->GetAimBones();
|
||||||
|
if (aim_bones.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (const auto& aim_bone : aim_bones)
|
||||||
|
{
|
||||||
|
auto& bone_transform = bone_nodes_[aim_bone.idx].local;
|
||||||
|
auto rotation = glm::angleAxis(-pitch * aim_bone.weight, glm::vec3(1.0f, 0.0f, 0.0f));
|
||||||
|
bone_transform.rotation = rotation * bone_transform.rotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void game::SkeletonInstance::UpdateBoneMatrices()
|
void game::SkeletonInstance::UpdateBoneMatrices()
|
||||||
{
|
{
|
||||||
for (TransformNode& node : bone_nodes_)
|
for (TransformNode& node : bone_nodes_)
|
||||||
|
|||||||
@ -17,6 +17,8 @@ public:
|
|||||||
const TransformNode& GetBoneNode(size_t index) const { return bone_nodes_[index]; }
|
const TransformNode& GetBoneNode(size_t index) const { return bone_nodes_[index]; }
|
||||||
|
|
||||||
void ApplySkelAnim(const assets::Animation& anim, float time, float weight);
|
void ApplySkelAnim(const assets::Animation& anim, float time, float weight);
|
||||||
|
void ApplyAim(float yaw, float pitch);
|
||||||
|
|
||||||
void UpdateBoneMatrices();
|
void UpdateBoneMatrices();
|
||||||
|
|
||||||
const std::vector<TransformNode> GetBoneNodes() const { return bone_nodes_; }
|
const std::vector<TransformNode> GetBoneNodes() const { return bone_nodes_; }
|
||||||
|
|||||||
@ -63,10 +63,13 @@ void game::view::CharacterView::Update(const UpdateInfo& info)
|
|||||||
|
|
||||||
// interpolate states
|
// interpolate states
|
||||||
float tps = 25.0f;
|
float tps = 25.0f;
|
||||||
float t = (info.time - update_time_) * tps * 0.8f; // assume some jitter, interpolate for longer
|
float t = (info.time - update_time_) * tps * 0.8f; // assume some jitter, interpolate for longer;
|
||||||
t = glm::clamp(t, 0.0f, 2.0f);
|
t = glm::clamp(t, 0.0f, 2.0f);
|
||||||
|
float t_sane = glm::clamp(t, 0.0f, 1.0f);
|
||||||
|
|
||||||
root_.local = Transform::Lerp(states_[0].trans, states_[1].trans, t);
|
root_.local = Transform::Lerp(states_[0].trans, states_[1].trans, t);
|
||||||
|
|
||||||
|
// loco
|
||||||
animstate_.loco_blend = glm::mix(states_[0].loco_blend, states_[1].loco_blend, t);
|
animstate_.loco_blend = glm::mix(states_[0].loco_blend, states_[1].loco_blend, t);
|
||||||
|
|
||||||
float loco_phase0 = states_[0].loco_phase;
|
float loco_phase0 = states_[0].loco_phase;
|
||||||
@ -75,6 +78,13 @@ void game::view::CharacterView::Update(const UpdateInfo& info)
|
|||||||
loco_phase0 -= 1.0f;
|
loco_phase0 -= 1.0f;
|
||||||
animstate_.loco_phase = glm::mod(glm::mix(loco_phase0, loco_phase1, t), 1.0f);
|
animstate_.loco_phase = glm::mod(glm::mix(loco_phase0, loco_phase1, t), 1.0f);
|
||||||
|
|
||||||
|
// action
|
||||||
|
animstate_.action_phase = glm::mix(states_[0].action_phase, states_[1].action_phase, t_sane);
|
||||||
|
|
||||||
|
// aim
|
||||||
|
animstate_.yaw = glm::mix(states_[0].aim_yaw, states_[1].aim_yaw, t_sane);
|
||||||
|
animstate_.pitch = glm::mix(states_[0].aim_pitch, states_[1].aim_pitch, t_sane);
|
||||||
|
|
||||||
animstate_.ApplyToSkeleton(sk_);
|
animstate_.ApplyToSkeleton(sk_);
|
||||||
|
|
||||||
root_.UpdateMatrix();
|
root_.UpdateMatrix();
|
||||||
@ -157,13 +167,17 @@ void game::view::CharacterView::OnAttach()
|
|||||||
bool game::view::CharacterView::ReadState(net::InMessage* msg)
|
bool game::view::CharacterView::ReadState(net::InMessage* msg)
|
||||||
{
|
{
|
||||||
update_time_ = world_.GetTime();
|
update_time_ = world_.GetTime();
|
||||||
|
|
||||||
|
auto& old_state = states_[0];
|
||||||
|
auto& new_state = states_[1];
|
||||||
|
|
||||||
// init lerp start state
|
// init lerp start state
|
||||||
states_[0].trans = root_.local;
|
old_state.trans = root_.local;
|
||||||
states_[0].loco_blend = animstate_.loco_blend;
|
old_state.loco_blend = animstate_.loco_blend;
|
||||||
states_[0].loco_phase = animstate_.loco_phase;
|
old_state.loco_phase = animstate_.loco_phase;
|
||||||
|
old_state.action_phase = animstate_.action_phase;
|
||||||
auto& new_state = states_[1];
|
old_state.aim_yaw = animstate_.yaw;
|
||||||
|
old_state.aim_pitch = animstate_.pitch;
|
||||||
|
|
||||||
if (msg)
|
if (msg)
|
||||||
{
|
{
|
||||||
@ -209,6 +223,40 @@ bool game::view::CharacterView::ReadState(net::InMessage* msg)
|
|||||||
new_state.loco_blend = sync_.loco_blend.Decode();
|
new_state.loco_blend = sync_.loco_blend.Decode();
|
||||||
new_state.loco_phase = sync_.loco_phase.Decode();
|
new_state.loco_phase = sync_.loco_phase.Decode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// action anim
|
||||||
|
if (fields & CSF_ACTION_ANIM)
|
||||||
|
{
|
||||||
|
if (!msg->Read(sync_.action_anim))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
animstate_.action_anim_idx = sync_.action_anim;
|
||||||
|
}
|
||||||
|
|
||||||
|
// action phase
|
||||||
|
if (fields & CSF_ACTION_PHASE)
|
||||||
|
{
|
||||||
|
if (!net::ReadDelta(*msg, sync_.action_phase))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
new_state.action_phase = sync_.action_phase.Decode();
|
||||||
|
|
||||||
|
if (fields & CSF_ACTION_ANIM)
|
||||||
|
{
|
||||||
|
// anim just changed, dont blend phase
|
||||||
|
old_state.action_phase = new_state.action_phase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// aim
|
||||||
|
if (fields & CSF_AIM)
|
||||||
|
{
|
||||||
|
if (!net::ReadDelta(*msg, sync_.aim_yaw) || !net::ReadDelta(*msg, sync_.aim_pitch))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
new_state.aim_yaw = sync_.aim_yaw.Decode();
|
||||||
|
new_state.aim_pitch = sync_.aim_pitch.Decode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@ -13,8 +13,14 @@ namespace game::view
|
|||||||
struct CharacterViewState
|
struct CharacterViewState
|
||||||
{
|
{
|
||||||
Transform trans;
|
Transform trans;
|
||||||
|
|
||||||
float loco_blend = 0.0f;
|
float loco_blend = 0.0f;
|
||||||
float loco_phase = 0.0f;
|
float loco_phase = 0.0f;
|
||||||
|
|
||||||
|
float action_phase = 0.0f;
|
||||||
|
|
||||||
|
float aim_yaw = 0.0f;
|
||||||
|
float aim_pitch = 0.0f;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CharacterViewClothes
|
struct CharacterViewClothes
|
||||||
|
|||||||
@ -139,6 +139,8 @@ using SoundPitchQ = Quantized<uint8_t, 0, 2>;
|
|||||||
using AnimBlendQ = Quantized<uint8_t, 0, 1>;
|
using AnimBlendQ = Quantized<uint8_t, 0, 1>;
|
||||||
using AnimTimeQ = Quantized<uint8_t, 0, 1>;
|
using AnimTimeQ = Quantized<uint8_t, 0, 1>;
|
||||||
|
|
||||||
|
using AnimAimAngleQ = Quantized<uint8_t, -PI_N, PI_N, PI_D * 2>;
|
||||||
|
|
||||||
using NumClothes = uint8_t;
|
using NumClothes = uint8_t;
|
||||||
using ClothesName = FixedStr<32>;
|
using ClothesName = FixedStr<32>;
|
||||||
|
|
||||||
|
|||||||
@ -147,14 +147,14 @@ inline bool ReadRGB(InMessage& msg, uint32_t& color)
|
|||||||
|
|
||||||
// DELTA
|
// DELTA
|
||||||
template <std::unsigned_integral T> requires (sizeof(T) == 1)
|
template <std::unsigned_integral T> requires (sizeof(T) == 1)
|
||||||
inline void WriteDelta(OutMessage& msg, T previous, T current)
|
inline void WriteDelta(OutMessage& msg, T current, T previous)
|
||||||
{
|
{
|
||||||
// 1 byte => just write current
|
// 1 byte => just write current
|
||||||
msg.Write(current);
|
msg.Write(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::unsigned_integral T> requires (sizeof(T) > 1)
|
template <std::unsigned_integral T> requires (sizeof(T) > 1)
|
||||||
inline void WriteDelta(OutMessage& msg, T previous, T current)
|
inline void WriteDelta(OutMessage& msg, T current, T previous)
|
||||||
{
|
{
|
||||||
static_assert(sizeof(T) <= 4);
|
static_assert(sizeof(T) <= 4);
|
||||||
|
|
||||||
@ -169,18 +169,18 @@ inline void WriteDelta(OutMessage& msg, T previous, T current)
|
|||||||
template <AnyQuantized T>
|
template <AnyQuantized T>
|
||||||
inline void WriteDelta(OutMessage& msg, T current, T previous)
|
inline void WriteDelta(OutMessage& msg, T current, T previous)
|
||||||
{
|
{
|
||||||
WriteDelta(msg, previous.value, current.value);
|
WriteDelta(msg, current.value, previous.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::unsigned_integral T> requires (sizeof(T) == 1)
|
template <std::unsigned_integral T> requires (sizeof(T) == 1)
|
||||||
inline bool ReadDelta(InMessage& msg, T previous, T& current)
|
inline bool ReadDelta(InMessage& msg, T& current, T previous)
|
||||||
{
|
{
|
||||||
// 1 byte => just read current
|
// 1 byte => just read current
|
||||||
return msg.Read(current);
|
return msg.Read(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <std::unsigned_integral T> requires (sizeof(T) > 1)
|
template <std::unsigned_integral T> requires (sizeof(T) > 1)
|
||||||
inline bool ReadDelta(InMessage& msg, T previous, T& current)
|
inline bool ReadDelta(InMessage& msg, T& current, T previous)
|
||||||
{
|
{
|
||||||
static_assert(sizeof(T) <= 4);
|
static_assert(sizeof(T) <= 4);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user