【Flutter&Flame Game - Lu】Carrera crítica | Uso de componentes de texto

¡Continúe creando, acelere el crecimiento! Este es el 7º día de mi participación en el "Nuggets Daily New Plan · June Update Challenge", haz clic para ver los detalles del evento


prefacio

Esta es una serie de tutoriales producidos por Zhang Fengjietlie y publicados en la comunidad de Nuggets. Flutter&FlameSi ve este artículo en otras plataformas, puede pasar a los Nuggets para verlo según el enlace. Debido a que el artículo puede actualizarse y revisarse, prevalecerá la versión del artículo de los Nuggets. Lista de artículos de esta serie:


1. El valor de las palabras

Ya sea una aplicación o un juego, 文字ya 图片es el eterno tema de exhibición. La civilización humana se basa en las palabras como herencia, y el mayor valor de las palabras radica en transmitir información. La siguiente es una captura de pantalla de la batalla de Onmyoji, en la que el daño del personaje, el rendimiento actual, la cantidad de fantasmas, si es crítico y el estado de supervivencia de Shikigami, etc., todo depende del texto para mostrar y retroalimentar información interactiva para el jugador.


Aquí, primero muestre la información del volumen de sangre en la barra de sangre, como se muestra a continuación:


2. Componente de texto - TextComponent

FlameEn , use el TextComponentwidget para mostrar texto. Sabemos Flutterque el TextPaintertexto se puede dibujar a través de la clase en el dibujo. De hecho, la TextComponentconstrucción es esencialmente TextPainteruna capa de encapsulación. Se proporciona una TextPaintclase para su uso.


El siguiente Liveablees el procesamiento en , solo cree un TextComponentobjeto y agréguelo usando el addmétodo . Además, cualquier componente add(RectangleHitbox)puede de , lo cual es conveniente para ver el área ocupada. Consulte el código: 【06/01】

final TextStyle _defaultTextStyle = const TextStyle(fontSize: 10, color: Colors.white);
late final TextComponent _text;

void initPaint({
  required double lifePoint,
  Color lifeColor = Colors.red,
  Color outlineColor = Colors.white,
}) {
  // 略...
  // 添加生命值文字
  _text = TextComponent(textRenderer: TextPaint(style: _defaultTextStyle));
  _updateLifeText();
  // 添加外框信息
  _text.add(RectangleHitbox()..debugMode = true); 
  add(_text);
}

void _updateLifeText(){
  _text.text = 'Hp ${_currentLife.toInt()}';
}
复制代码

Como se puede ver a continuación, por defecto TextComponentestá alineado con la esquina superior izquierda del área principal. Además, TextComponenttambién es una PositionComponentfamilia de componentes, y podemos realizar operaciones como traslación, escalado y rotación sobre ella.


Por ejemplo, lo siguiente se tag1coloca junto al especificado, el_text lado izquierdo se alinea con la barra de estado y encima de la barra de estado:position

// 添加生命值文字
_text = TextComponent(textRenderer: TextPaint(style: _defaultTextStyle));
_updateLifeText();
double y = -(offsetY+_text.height+2);
double x = (size.x/2)*(1-widthRadio);
_text.position = Vector2(x, y); // tag1
add(_text);
复制代码

Después de eliminar el cuadro de información, se muestra de la siguiente manera, y la vida se reduce cuando se hace clic. Se puede mostrar _updateLifeTextactualizando texto:


3. Mostrar datos de daños

Cuando el monstruo es atacado, los datos de daño generalmente se mostrarán para permitir que el operador tenga una experiencia más intuitiva. Ahora se espera que cuando un monstruo resulte herido, la cantidad de daño se muestre a la izquierda y el daño desaparecerá automáticamente 1sdespués de que se mantenga la . Como se muestra a continuación: ver el código [06/02]

伤害数据是在 Liveable 中维护的,虽然可以直接在 Liveable 中添加文字。但这样的话会使得 Liveable 的职能过于复杂,也不利于后续的拓展。我们可以单独定义一个 DamageText 构件,来维护伤害数值的显示逻辑。
如下代码所示,在 Liveable 中添加一个 addDamage 的方法,在 tag1 处添加 damageText 文字。然后使用 Future.delayed 方法,延迟 1s 中,调用 damageText.removeFromParent 方法即可移除。

class DamageText extends PositionComponent{

  final TextStyle _damageTextStyle = const TextStyle(
      fontSize: 14,
      color: Colors.white,
      fontFamily: 'Menlo',
      shadows: [
        Shadow(color: Colors.red, offset: Offset(1, 1), blurRadius: 1),
      ]);

  Future<void> addDamage(int damage) async {
    TextComponent damageText =
    TextComponent(textRenderer: TextPaint(style: _damageTextStyle));
    damageText.text = damage.toString();
    damageText.position = Vector2(-30, 0);
    add(damageText); // tag1
    await Future.delayed(const Duration(seconds: 1));
    damageText.removeFromParent();
  }

}
复制代码

这样在 Liveable 中就不必处理具体添加伤害文字的逻辑,只需要通过 DamageText 来管理即可。比如在 loss 方法中,当角色受到伤害,通过 _damageText.addDamage 来添加伤害文字,这样处理就非常方便。想要对伤害文字进行显示进行修改或拓展,直接在 DamageText 处理即可,这就是职责的分离。

final DamageText _damageText = DamageText();

void initPaint({
  required double lifePoint,
  Color lifeColor = Colors.red,
  Color outlineColor = Colors.white,
}) {
	// 略...
  add(_damageText);
}

void loss(double point) {
  _damageText.addDamage(-point.toInt());
  // 略...
}
复制代码

4. 暴击伤害

这里来模拟一下产生暴击的情况:如下图所示,伤害时有一定概率产生暴击,此时使用另一种文字样式。并给出 暴击 的字样提示:代码见 【06/03】

实现也比较简单,在 addDamage 中,传入 isCrit 的入参,区分是否暴击。如果是暴击,使用 _addCritDamage 进行处理,添加黄色伤害和暴击字样即可。

---->[DamageText]----
void addDamage(int damage,{bool isCrit = false}) {
  if(!isCrit){
    _addWhiteDamage(damage);
  }else{
    _addCritDamage(damage);
  }
}

Future<void> _addCritDamage(int damage) async {
  TextComponent damageText =
  TextComponent(textRenderer: TextPaint(style: _critDamageTextStyle));
  damageText.text = damage.toString();
  damageText.position = Vector2(-30, 0);
  TextStyle style = _critDamageTextStyle.copyWith(fontSize: 10);
  TextComponent infoText = TextComponent(textRenderer: TextPaint(style:style ));
  infoText.text = '暴击';
  infoText.position = Vector2(-30+damageText.width-infoText.width/2, -infoText.height/2);
  add(infoText);
  add(damageText);
  await Future.delayed(const Duration(seconds: 1));
  infoText.removeFromParent();
  damageText.removeFromParent();
}
复制代码

暴击和爆伤,本应是角色的属性,这里暂时不搞这么复杂,在 Liveableloss 方法中,用 75% 暴击和 165% 爆伤进行简单的测试,代码如下:

---->[Liveable]----
final Random _random = Random();

void loss(double point) {
  double crit = 0.75;
  double critDamage = 1.65;
  bool isCrit = _random.nextDouble() < crit;
  if (isCrit) {
    point = point * critDamage;
  }
  _damageText.addDamage(-point.toInt(),isCrit: isCrit);
  // 略...
}
复制代码

5.多次伤害

1sEl daño desaparece después de .Cuando el daño continuo está dentro de un segundo, o cuando se agregan múltiples daños a un daño, se producirá una oclusión. Por lo tanto, es necesario realizar el procesamiento de compensación en daños múltiples, y el efecto es el siguiente: consulte el código [06/04]


Aquí se explota una característica Componentde , cada uno Componenttiene una childrenpropiedad que representa una colección de subcomponentes. addDamageEn , solo necesita obtener el último elemento de acuerdo con la colección para determinar el desplazamiento del texto agregado:

void addDamage(int damage,{bool isCrit = false}) {
  Vector2 offset = Vector2(-30, 0);
  if(children.isNotEmpty){
    final PositionComponent last;
    if(children.last is PositionComponent){
      last = children.last as PositionComponent;
      offset = last.position + Vector2(5, last.height);
    }
  }
  if(!isCrit){
    _addWhiteDamage(damage,offset);
  }else{
    _addCritDamage(damage,offset);
  }
}
复制代码

El texto en sí es relativamente simple, pero el mantenimiento de datos y el procesamiento lógico relacionado con el texto siguen siendo muy complicados. Este artículo explica brevemente cómo se utiliza el texto mostrando la 生命值suma de los roles . 伤害值En los juegos en general, las imágenes se utilizan como texto, como los números de daño de Onmyoji. Recuerde que debe haber herramientas para que las imágenes formen fuentes, como 6000la cadena de , que corresponderá a las imágenes relevantes en la fuente. Sin embargo, se siente que el flamemarco es demasiado simple y no debería ser apoyado.

Eso es todo por este artículo, nos vemos mañana~


\

Supongo que te gusta

Origin juejin.im/post/7103690366536122399
Recomendado
Clasificación