C++游戏开发之旅 23

问题概述

现在我们已经实现了完整的移动系统,但是游戏手感上还是有缺陷的,玩家操作的时候会遇到某些问题。

最直接的就是,在进行平台跳跃的时候,我们跳跃判定是很严格的,比如只有在Idle、walk状态下进行,还有就是在平台边缘极限跳跃的时候,感觉明明按下跳跃了,但是依然失败了。

其次,玩家在下斜坡的时候,因为没有接触到地面,状态会有一个断续的改变,角色似乎在抖动;

还有就是玩家无敌状态下不知道持续到什么时候结束,缺少视觉反馈。

解决方案:游戏手感优化

土狼时间(Coyote Time): 允许玩家在离地后短暂时间内仍然可以跳跃

为了解决玩家在下斜坡时候状态一直切换的问题,我们需要更严格的判断,在从Idle和Walk状态进入Fall时,需要满足土狼时间超时 && 离地

无敌闪烁:添加一个计时器

1.添加土狼时间

我们需要在player_component.h中定义土狼时间和计时器。

// 土狼时间
static constexpr float coyote_time_ = 0.15f;
float coyote_timer_ = 0.0f;

bool is_on_ground() const;

// player_component.cpp
bool PlayerComponent::is_on_ground() const
{
    return coyote_timer_ < coyote_time_ || physics_component_->hasCollidedBelow();
}

这里会新增一个函数is_on_ground(),只有当超过土狼时间并且物理组件不和下边缘进行碰撞的情况下,才判定为离开地面

当然,在player_component中的update需要累加土狼时间。

void PlayerComponent::update(float dt, engine::core::Context &context)
{
    // 如果离开地面,土狼时间开始计时,一段时间后才表示离开地面
    if(!physics_component_->hasCollidedBelow()){
        coyote_timer_ += dt;
    } else { // 如果接触地面,土狼时间重置
        coyote_timer_ = 0;
    }

    if(!current_state_) return;

    auto new_state = current_state_->update(dt, context);
    if(new_state){
        changeState(std::move(new_state));
    }
}

设置完后,我们需要对Idle和Walk两个状态中的update进行修改,将原来的判断player_component_->getPhysicsComponent()->hasCollidedBelow()改为判断is_on_ground()

if(!player_component_->is_on_ground()){  // 没有和下方发生碰撞,则进入下落状态
    return std::make_unique<FallState>(player_component_);
}

这样我们就解决了玩家下斜坡状态抖动的问题,以及边缘极限跳跃失败的问题。

2.无敌闪烁

我们需要定义无敌闪烁时间以及计时器

// 无敌闪烁
static constexpr float flash_interval_ = 0.1f;
float flash_timer_ = 0.0f;

我们是通过不断设置精灵组件中的是否显示来实现闪烁效果的,整体比较简单。

void PlayerComponent::update(float dt, engine::core::Context &context)
{
    // 如果离开地面,土狼时间开始计时,一段时间后才表示离开地面
    if(!physics_component_->hasCollidedBelow()){
        coyote_timer_ += dt;
    } else { // 如果接触地面,土狼时间重置
        coyote_timer_ = 0;
    }

    // 如果无敌, 则闪烁
    if(health_component_->isInvincible()){
        flash_timer_ += dt;
        // 这里定义了一个周期0.2s
        if(flash_timer_ > flash_interval_ * 2){
            flash_timer_ -= flash_interval_ * 2;
        }
        // 在半个周期内闪烁, 如果小于半个周期, 则隐藏, 否则显示
        if(flash_timer_ < flash_interval_){
            sprite_component_->setHidden(true);
        } else {
            sprite_component_->setHidden(false);
        }
    } else { // 如果不在无敌状态, 则显示
        sprite_component_->setHidden(false);
    }

    if(!current_state_) return;

    auto new_state = current_state_->update(dt, context);
    if(new_state){
        changeState(std::move(new_state));
    }
}

我们定义了一个周期,为0.2s,当计时器小于半个周期,则隐藏,大于则显示;超过一个周期则重置。当然如果当前角色不在无敌时间内,需要保证其显示。这样1s内有5个周期,会闪烁5次。

编译运行

游戏手感好了许多。

posted @ 2026-03-04 20:48  wenyiGamecpp  阅读(36)  评论(0)    收藏  举报