ムラサメ研究ブログ

主にゲームやプログラミングのログ

Overwatch 魔境でのピックを考える

対象

FPS初心者。ランクマで1000未満の初心者

魔境では基本がトロールピックである

魔境ではノーヒーラー、ノータンク、オールアタッカー というのは良くある
たいてい 相手がヒーラータンクいると負けるので、必然的に自分がヒーラータンク選ぶと勝率が上がるはずである

また、魔境ではジャンクラットの人気が高い。ほぼ両チームに必ずいる
他にも ゲンジ、ハンゾー、ウィドウ、ドゥームフィストの使用率も高い
それらに関しても 考慮する

まず、自分のデータより魔境向きなキャラを考える

使用時間

22時間 ソルジャー
8時間 Dva、マーシ―
4時間 ルシオ、アナ、リーパー、トレーサ 
2時間 ウインストン、ロードホッグ、マクリー、ザリア、ファラ、ゼニヤッタ、ジャンクラット、ソンブラ 
1時間 メイ、トールビョン、ハンゾウ、ラインハルト 
1時間未満 オリーサ、バスティオン、シンメトラ、ウィドウメーカー、ゲンジ、ドゥームフィスト 

最初はFPSの標準的な操作であるソルジャーをよく使った。
魔境ではヒーラータンクが不足しているので Dvaマーシールシオ、アナの使用率も高い
ウィドウ、ゲンジ等はあまり使っていない

勝率

93%   トールビョン
60%後半 ルシオ、シンメトラ
50%後半 マーシ―、ソンブラ
50%前半 ハンゾー、ラインハルト
40%後半 ゲンジ、オリーサ、Dva、バスティオン、ロードホッグ
40%前半 ウインストン、ウィドウ、ファラ、アナ、ゼニヤッタ
30%後半 メイ、トレイサー、リーパー
30%前半 マクリー、ソルジャー
20%後半 ジャンクラット、ザリア、ドゥームフィスト

やはり オートエイムで、頭脳を使ってタレットを設置できるトールビョンの勝率がおかしいほど高い
オートエイムで あまりタレットを破壊に来ないため 魔境ではトールビョンが強いと思う
実際に相手したときに嫌である

ルシオ、マーシ―は勝率高いというより、ヒーラーが居ない事 が多い魔境では、ヒーラーをピックするだけで
勝率が上がるのは当然である

シンメトラも やはりタレットの関係で初心者でも強い

ハンゾーは何故だろう。意外とキルできる・・
ラインハルトは やはり盾は重要ということだろう
魔境では盾の後ろから撃つ人も少ないのだけど、敵からしてみると 盾が邪魔で壊しに来るので アタックがしやすいのだろう

マクリー、ソルジャー
遠距離アタッカーが下手なんだと思う。あるいは これらを出す時はファラ対策なので、魔境ではファラが強いという事も考えられる

ジャンクラット、ザリア、ドゥームフィスト データ足りないものもあるけど 勝率悪すぎ
ザリアはほんと素人に難しい。ドゥームフィストはまだ使い方わかってない。ジャンクラットは最初のころ使ってたので今だともっと勝率上がるとおもう

総論

トールビョン、シンメトラ などのタレットは初心者向け
2-2-2の構成を目指し ヒーラーかタンク使うと勝ちやすい
ファラ対策が出来ない人が多いので ファラは無双しやすい
ラインハルトは なんだかんだ強い

練習すべきキャラ

ヒーラーとタンク

ヒーラーでいえば ルシオ、ゼニ、マーシ―あたりが使えるようになりたい
タンクだと ハルト、ザリア あるいは Dva、ゴリラ。
つまり ほぼ全部必要だね・・・

ファラ

やっぱファラは魔境では刺さる事が非常に多いので絶対覚えるべき
特に 必ずピックされるしダメージ高くてうざい ジャンクラットや
タンクを瞬殺リーパーあたりにはめっぽう強い
魔境ではタレット処理にも役立つ

対ファラ

という事でファラに対抗したいが なかなか難しい
一般レベルだと ソルジャー、マクリーだが、ファラマーシーや うまいファラ相手だと倒すの難しい
ウィドウやアナをピックするのも考えるか・・

UNITYでDepthをリニアでEXR形式で出力

概要

Depthマップ欲しい事は多いと思います Depthの取得自体は簡単ですが、Depthの計算上 Z値とリニアで変化しないため感覚的にわかりにくいので リニアにしたい DepthをPNGファイルにすると 256段階に丸められてしまうため、浮動小数点形式で保存したいので EXR形式で保存したい Depthの取得は、別パスにて画面全体から取得する。UNITYではポストプロセスで行うのが楽

UNITYちゃん

今回は題材にUNITY Chanを使う 何を使ってもいいし、UNITYの3DMeshでもいい

ポストプロセス

デプスは画面全体を取得する必要があるので、ポストプロセスで取得する ポストプロセスを使うには カメラに OnRenderImage関数の入ったスクリプトをつける

using System.IO;
using UnityEngine;
using UnityEngine.Rendering;

[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class SceneScript : MonoBehaviour {

    public Material mat;

    [ImageEffectOpaque]
    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, dest, mat);
    }
}

これで、マテリアルに設定したシェーダーを使い ポストプロセスを実行すると

シェーダーでDepth取得

マテリアルに設定するシェーダーを作成する

まず、デプステクスチャを有効にするため、シェーダーの変数宣言のあたりに

UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);

を。これでデプステクスチャに _CameraDepthTexture でアクセスできる

Shader "test/CopyDepth" {
  SubShader {
    ZTest Always
    Cull Off
    ZWrite Off
    Fog{ Mode Off }
    Tags{ "RenderType" = "Opaque" }

    Pass {

      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag
      #include "UnityCG.cginc"
      #pragma only_renderers d3d9 d3d11 glcore gles gles3 metal xboxone ps4 
      #pragma target 3.0

      UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
      
      struct v2f {
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
      };

      v2f vert (appdata_img v) {
    v2f o = (v2f)0;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord.xy);
    return o;
      }

      float frag(v2f i) : SV_TARGET{
        return SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
      }
    ENDCG
    }

  }
}

上記シェーダーをポストプロセスに設定すると、Depthっぽいものが表示される (画面表示はこの段階では崩れているが) f:id:murasame-labo:20170805005638p:plain

ただしいDepthを取得

Depthテクスチャ用に RenderTextureを生成し、ポストエフェクト時にそこにコピーする

using System.IO;
using UnityEngine;
using UnityEngine.Rendering;

// deplicated
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class SceneScript : MonoBehaviour {

    public Material mat;
    private RenderTexture _rtd;


    // Use this for initialization
    void Start() {
        GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;

        _rtd = new RenderTexture(2048, 2048, 32);
    }

    
    [ImageEffectOpaque]
    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
       Graphics.Blit(source, _rtd, mat);
    }
}

見てもわからないが、ポストエフェクトで画面に出力せず、 rtdへコピーされているので rtdの値を出力すればよい

EXRで保存

float形式で保存するために EXR形式を使う UNITYでは EXRへの変換は簡単にできる Aを押したらファイルに保存する

    void Update () {
        if (Input.GetKey(KeyCode.A)){
            RT2EXR(_rtd,  "rtd.exr");
        }
    }

    void RT2EXR(RenderTexture rt, string filename)
    {
        Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGBAHalf, false);
        RenderTexture.active = rt;
        tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
        tex.Apply();

        byte[] bytes = tex.EncodeToEXR();
        Object.Destroy(tex);

        File.WriteAllBytes(filename, bytes);

    }

これで exrファイルとして出力できた f:id:murasame-labo:20170805010655p:plain

リニア化

上記のままでは、Depthの値はZとリニアではないので 直観的にわかりやすく リニアに変換する UNITYには便利な組み込み関数があるので それでリニアにできる

// Z buffer to linear 0..1 depth
inline float Linear01Depth( float z )
{
  return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);
}

これでzバッファの値をリニアに変換できる _ZBufferParams 等はシェーダーのビルトイン変数といい、UNITYがConstantBufferに送っている

    // x = 1 or -1 (-1 if projection is flipped)
    // y = near plane
    // z = far plane
    // w = 1/far plane
    float4 _ProjectionParams;

    // x = width
    // y = height
    // z = 1 + 1.0/width
    // w = 1 + 1.0/height
    float4 _ScreenParams;

    // Values used to linearize the Z buffer (http://www.humus.name/temp/Linearize%20depth.txt)
    // x = 1-far/near
    // y = far/near
    // z = x/far
    // w = y/far
    // or in case of a reversed depth buffer (UNITY_REVERSED_Z is 1)
    // x = -1+far/near
    // y = 1
    // z = x/far
    // w = 1/far
    float4 _ZBufferParams;

    // x = orthographic camera's width
    // y = orthographic camera's height
    // z = unused
    // w = 1.0 if camera is ortho, 0.0 if perspective
    float4 unity_OrthoParams;

具体的には上記のような、near far等の値が入っている

シェーダーを少し変更する

float frag(v2f i) : SV_TARGET{
  return Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv).x ) ;
}

これでリニアになったはずである(多分)

f:id:murasame-labo:20170805011402p:plain

まとめ

using System.IO;
using UnityEngine;
using UnityEngine.Rendering;

[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class SceneScript : MonoBehaviour {

    public Material mat;
    private RenderTexture _rtd;


    // Use this for initialization
    void Start() {
        GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;

        _rtd = new RenderTexture(2048, 2048, 32);
    }

    
    [ImageEffectOpaque]
    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, _rtd, mat);
    }

    // Update is called once per frame
    void Update () {
        if (Input.GetKey(KeyCode.A)){
            RT2EXR(_rtd,  "rtd.exr");
        }
    }

    void RT2EXR(RenderTexture rt, string filename)
    {
        Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGBAHalf, false);
        RenderTexture.active = rt;
        tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
        tex.Apply();

        byte[] bytes = tex.EncodeToEXR();
        Object.Destroy(tex);

        File.WriteAllBytes(filename, bytes);
    }
}
Shader "test/CopyDepth" {
  SubShader {
    ZTest Always
    Cull Off
    ZWrite Off
    Fog{ Mode Off }
    Tags{ "RenderType" = "Opaque" }

    Pass {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag
      #include "UnityCG.cginc"
      #pragma only_renderers d3d9 d3d11 glcore gles gles3 metal xboxone ps4 
      #pragma target 3.0

      UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);

      struct v2f {
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
      };

      v2f vert (appdata_img v) {
    v2f o = (v2f)0;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord.xy);
    return o;
      }

      float frag(v2f i) : SV_TARGET{
        return Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv).x ) ;
      }
      ENDCG
    }
  }
}

弱点&改善点

まず UNITYの調査してるんだけど そろそろまとまったので、書けるかな・・・・と。。 はよ プログラム記事かかないとなー ここは技術的なブログなのに

今の状況

FPS初めてプレイしたので、超下手で何をすべきかわかってないのでランクが500程度からのスタート これは ランクマッチに参加している人のなかで 下から1%のクソザコです プレイ時間としては30時間ほどなので、プレイ時間少なすぎてまだまだだけど 今の予想ではランク1000ぐらいの実力にはなったかなと思う これからランクマッチも頑張っていきたいところだけどまずは考察。

ランク1000以下の魔境では、リスポーンポイントでバスケしたり、ヒーラーなしの構成や、アサルトしばりのスナイパーとか 色々とひどい事もあるし、逆にサブアカウント、AIMボットなど 強すぎるのもいる これは確率的に半々あるとして 事故扱い

事故をのぞくと 自分の能力が今は600なので自分の能力が1000だとすると、平均ランクで80ぐらい負けていても互角のはずだ

弱点

AIM能力が低い。プレイヤースキルはすぐには上達しないので 1つずつ対策していこうと思う

マウス感度

マウス感度設定を調整しよう プレイ環境もだいたい決まったし、トレーニングモードでマウス感度調整しようと思う

逃げ方

逃げるときに真後ろに下がっていて ターゲットされやすい。特にスナイパーに殺されまくるので 逃げるときは頭の位置を動かすよう 斜めジャンプしながら撤退しよう 斜めジャンプも 左右交互や片方だけなど読まれないようにしよう

攻撃時

攻撃時に怖くて真後ろに退きながら攻撃する癖がある事がわかった・・ だいたいは相手は距離をとるのだし 前進するぐらいのつもりで行ってもいいかもしれない ただし深追いしないように

フォーカス

味方とフォーカス合わせる。2v1などこちらが多い状態を作る

敵遭遇時

敵を見た瞬間に焦ってトリガーをひく癖があるので やめよう。なかなかやまらないけど・・ 特にトレイサーやDVA 得意距離まで間合いをつめてから撃たなければ 相手の距離で撃ったらまける 相手が気づいてなければ距離をつめて攻撃、気づいてれば逃げる

また、AIMあわせずにとりあえず撃つ癖があるので、クロスファイアをあわせてから撃つようにする!

敵遭遇時のAIM対策

スナイパーとか ハンゾーしてみてもいいのか?

裏どりしたときのフォーカス

ヒーラー優先

殺しきる

やれる時はとどめをさす。中途半端に当てて逃げさせては損 特に裏どりに対しては こちらはヒーラー付き、相手は撤退にリスクがあるので 圧倒的に有利なはずだ

ULTの使い道

よーく考えよう・・ 時には1匹始末のために使ってもいい(龍神の剣やバレッジに対して タクティカルバイザーとか) 2匹倒せばいいほう

味方を見る

敵に撃たれた時など 無条件で交戦したり逃げてるが やばいと思ったら味方をみて 撤退か追い打ちか考える 見て死んでもいい。とりあえず行動前に味方見る

ロール覚える

いまいちわかってないけど、今考えているのは下記 ただし 例えばタンクでも ウインストンやdvaのような、遊撃が得意なタンクは、前線を張らない事も多い

タンク

盾になる。盾は自分を守らない 味方を守る。なぜなら自分を守っても反撃できず割られるだけ。味方に盾をはれば味方が攻撃してくれるor安全に撤退できる ので 守る対象は主にアタッカー ヒーラーはそもそも 攻撃食らう場所にいるのが間違い

ヒーラー

回復をする。もちろんタンクメイン 一歩下がった位置から回復 と同時に 裏どりなどを常に気を配るべき 全員をヒールしようとしない。敵陣に突っ込んだ奴は 無理に追いかけない

DPS

盾の裏から一方的に攻撃したり、敵の裏から襲撃したり、ヒーラーを狙いに来たDPSを追い払う 状況次第で色々と変わるから難しいね

アンチピック

これは 勉強していこう・・

魔境で刺さるキャラピック

ファラ:ソルのAIMが下手、ジャンクラ多い、ゲンジ多い、爆風こわい トールビョン:圧倒的に正確なAIM バスティオン:気付くのが遅いので 気が付いたら死んでる

念のため上記キャラの練習

とりあえず 上記を気を付けるための メモ

RenderTextureを使いレンダリング結果をTextureに保存する

レンダリング結果をテクスチャでほしい事ってありますよね

例えばレンダリング結果を鏡にうつしたり G-Bufferをファイルに保存したり

ってことで、RenderTextureを作り カメラにAttachし、レンダリング結果を取得します

まず RenderTextureをCreateします MultiRenderTarget機能があるので、最大8個まで取得できます

newでRenderTextureを作成しますが、幅、高さをピクセルサイズで指定し、ビット深度を指定します 幅、高さは2のn乗にしておきましょう

そして カメラにたいして SetTargetBuffersでアタッチします

なんとこれだけ。簡単ですね

    private RenderTexture[] _rt;
    public Camera cam;


    // Use this for initialization
    void Start() {
        _rt = new RenderTexture[8];
        for (int i=0; i< _rt.Length; i++)
        {
            _rt[i] = new RenderTexture(2048, 2048, 32);
        }

        cam.SetTargetBuffers(new RenderBuffer[8] { _rt[0].colorBuffer, _rt[1].colorBuffer, _rt[2].colorBuffer, _rt[3].colorBuffer
            , _rt[4].colorBuffer, _rt[5].colorBuffer , _rt[6].colorBuffer, _rt[7].colorBuffer}
            , _rt[0].depthBuffer);      
    }

2年前に開発したC++のリアルタイムゲームサーバ

はじめに

2年半ほどまえに、リアルタイム対戦ゲームの ソーシャルサーバとリアルタイムサーバの作成案件をもらった ソーシャルサーバは何度か作ったし余裕 リアルタイムサーバは、いわゆるMO的なゲームでロビーサーバを作ったり、レートでマッチングしたり、プレイヤーが落ちた時、復帰 と、考えることがいっぱいあったので 悩んだが 私なら何でも出来ると請けた

ソーシャルサーバはJavaPlayframeworkを使って問題なく設計したが リアルタイムサーバは C++で! という要望だった C++のりゆうは 速度が速い事と、運用会社が得意だからと

Windowsソケットとマルチスレッドを使った マルチスレッドパターンのライブラリをもらい 自由に使ってください との依頼だった

会社の資産をもらえ、それに沿って作れば簡単だし、責任もなすりつけられるので それでいこうと思ったが やはり 大量接続を考えると 非同期で行うほうが効率は良い それに もらったライブラリは、当然ながらデータアクセス部がMutexで同期をとり、明らかなボトルネックがあった そのため、ライブラリの提供は丁寧にお断りし Boost.ASIOで1から作る事にした

非同期I/Oを使うため、Mutexも不要。 明らかに高速で動く

現在の状況

半年ほどで私は開発を終わり、その後少し機能追加はしたものの、ゲーム側の不具合だらけで サーバを先にリリースして抜けた その後2年の歳月がながれ、いよいよクライアントの不具合も解消され リリースされた iphone無料ゲーム 最高20位と好調な滑り出しで そこそこアクセスがきているが ソーシャルサーバは Javaだけに速く、ほとんどスケールアウトもせず 安定稼働 リアルタイムサーバは なんと あまりにも速すぎて、MAXのピークがきても 負荷が0.0のままで変わらず 非常に安定しているとのこと

やはり C++で作ってよかった。予想では nodeで作った場合の100倍以上。PHPの5000倍ぐらいはパフォーマンス出ると思う

正直いうと、元から速かったので、最終的なチューニングも行ってないので、チューニングすれば3割以上は速くなると思うし 今作ると チューニングなしで3割速く作れると思う

設計

設計は リアルタイムサーバは余裕だった。 問題点は事前調査しきれないので、トライ&エラーだ ソーシャルサーバのほうはDB構成などあまり得意じゃないので 友達に作ってもらい、それを修正した

C10K問題というのがバズってたので、あくまで 非同期I/Oで処理することを考えた

非同期にはboost.asioを使用し、File I/Oや Redis その他のI/Oも非同期にした また、勉強もかねて C++11やboostを積極的に取り入れた

ルームへの復帰も簡単にできるよう、ルームの検索も高速になるよう、ルームリストとユーザーリスト作ってキャッシュさせる もちろん お互いの参照もあるのでWeakPtr使いつつ

非同期処理はコールバック地獄になるので、coroutineを使い簡略化した

当時 Earlangも気になっていたので、例外が起きたら例外を処理せず死ぬように作ったため、例外はシンプルになる もちろん 死ぬ前にルーム退出やAIへ自動的に変わるなど 後処理はあるけど 基本的に 何かエラーがあれば、コルーチンが死ぬという簡単な作りにした

その他、関数型言語ならい、参照透過性を意識して作った

苦労ポイント

ルーム

一番開発時に不具合が出た・・ 理由は shared_ptrを使い管理していたのだが 参照カウントが残ることが多かった プログラムのミスなんだけど、高速化のための List等に残っていたり・・ 逆にいえば Listからの消し忘れなどが shared_ptrを使っているために、簡単に発見できたといえる もし生ポインタ使っていれば、Listに無効なポインタが残って メモリリークをしていたかもしれない

タイムアウト

ASIOのタイムアウト処理が面倒だし、油断すると 無限に待ったりする タイムアウトの簡略化が課題

ほかプレイヤーの同期

例えば ゲームの開始など、全プレイヤーのOKの帰りを待つ必要があるが、そのあたりのSyncが難しい それに 麻雀ゲームという性質上、牌を切るごとに 全員の反応を待つ Syncが入る ので、それをライブラリ化したら 楽になった ただし 上記のタイムアウトと併用するので ややこしく・・ だが、このプロジェクトで最も頭を悩ませた問題だった・・・ 2週間ほど この部分で悩み、リアルに血を吐きかけた 逆に言えば ここをクリアしたらあとは力で勝負できたので楽だった

C#コードの移植

お客さんのC#コードの移植という作業があった が、お客さんのコードが Staticおじさんで、そのままでは複数プレイヤーに対応できないとか コードがあまりにも原始的だったり C#C++の違いだったり いろいろと苦労はしたけど 基本的には力業だった 逆に もっと苦労すると思っていたのだが、C++STLが予想以上に 高性能で、移植は楽だった

さいごに

WebサーバもC++で作ったし、C++は慣れた人が書くと、JavaC#で書くのと何も変わらない それでいて速度が100倍とか差が出るので 高速サーバには絶対おすすめ!

金額でいうとサーバコストが数十倍の単位で違ってくる

阿里山vs梨山

阿里山梨山。どちらも日本人には人気の 高級高山茶です 今回は2つの山の 青心烏龍種のお茶の飲み比べ

左 X-005阿里山  右 X-007 梨山 どちらも原価 100g1000円。日本だと30-50gで2500円ぐらい。 とっても高級なお茶です

見た目で違いがあります 阿里山のほうが少し焙煎が強いですね 茶葉の大きさは大体同じそう

淹れたときも一目瞭然 阿里山のほうが焙煎が強いため濃い水色です

味も当然 阿里山のほうが焙煎が強く香ばしい味 梨山のほうは 甘み旨みが強く緑茶に近い味わいです

どちらが好きかはお好みですが 梨山のほうがアイスには合いそうですね 7杯ぐらいまではおいしく飲めます

阿里山はだいたい標高1500メートルあたりでお茶が作られます 梨山はもう少し高く 1700メートル付近

梨山台中市の福壽のものです。梨山でも人気の茶園ですね 阿里山は嘉儀懸の樟樹湖。これも阿里山のなかでは最も有名な茶園ですね

青心烏龍、金萱茶 飲み比べ

はじめに

お茶好きです お抹茶、グリーンティー、紅茶なども好きですが 特に 中国茶が好きです その中でも特に、台湾ウーロン茶とプーアル茶が好き

原価屋

よく台湾や香港、中国でお土産にお茶を買ってきたり、日本のお茶屋さんで茶葉を買いますが 少量だと割高だし、もっと飲みたいときに飲めない 300g~とかで買うと、いろんな種類を飲みたいので飲みきれない ので、個人販売をしたいと思いました 飲みきれないぶんを個人販売したいなと。

ふつうは販売するときは利益率で計算します たとえば100g原価500円のお茶を原価率20%で販売したばあい 2500円です 原価5000円のお茶は 25000円 茶葉が高級になればなるほど 線形で金額が高くなるため 主に安い茶葉 あるいは、高級茶葉は少量ロットの販売になります

私は、お茶の葉の販売業をしたいわけではなく 自分で飲みたいお茶を飲んで、飲みきれないぶんを分けたいので 基本的には、珍しいお茶や高いお茶を出したい

ので、原価率では販売せず 原価+固定の手数料 という形にすれば たとえば原価500円の茶葉は2500円 原価5000円だと7000円 と、高い茶葉が割安で買えるはず

ってことで、茶葉の比較を

金萱烏龍茶(J-001)vs青心ウーロン茶(X-004)

どちらも100gあたり500円程度の同じランクのお茶

左が金萱、右が青心 茶葉の見た目は似ています

同じ量、同じ湯量、同じ温度で淹れたけど、見た目みても水色の違いがあります 青心のほうが明らかに濃い色をしています 焙煎も少し青心のほうが強いです

味のほうも明らかに違い 青心のほうが濃い、うまみの強い味です。一般的にナッツ臭といわれる香り うまみと強い甘み(中国では甜といいます)があります

金萱はさわやかな香り。ほんのり甘さ(甜)があります よくミルクの香りと言われます