Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
『GRANBLUE FANTASY: Relink』の長期開発を支え続けたテスト自動化の取り組み紹介
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Cygames, Inc.
PRO
December 18, 2024
Technology
610
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
『GRANBLUE FANTASY: Relink』の長期開発を支え続けたテスト自動化の取り組み紹介
Cygames, Inc.
PRO
December 18, 2024
More Decks by Cygames, Inc.
See All by Cygames, Inc.
【U/Day Tokyo 2025】Cygames流 最新スマートフォンゲームの技術設計 〜『Shadowverse: Worlds Beyond』におけるアーキテクチャ再設計の挑戦~
cygames
PRO
4
15k
【CEDEC+KYUSHU2025】学生・若手必見!テクニカルアーティスト 大全 ~仕事・スキル・キャリアパス、TAの「わからない」を徹底解剖~
cygames
PRO
1
1.2k
【TiDB User Day2025】リリース時のアクセス急増をいかにしてノーメンテで乗り越えたか 〜『Shadowverse: Worlds Beyond』におけるTiDB採用のゲームサーバー設計〜
cygames
PRO
1
2.8k
【CEDEC2025】『Shadowverse: Worlds Beyond』二度目のDCG開発でゲームをリデザインする~遊びやすさと競技性の両立~
cygames
PRO
2
850
【CEDEC2025】大規模言語モデルを活用したゲーム内会話パートのスクリプト作成支援への取り組み
cygames
PRO
2
2.5k
【CEDEC2025】現場を理解して実現!ゲーム開発を効率化するWebサービスの開発と、利用促進のための継続的な改善
cygames
PRO
0
1.8k
【CEDEC2025】ブランド力アップのためのコンテンツマーケティング~ゲーム会社における情報資産の活かし方~
cygames
PRO
0
1.9k
【CEDEC2025】『ウマ娘 プリティーダービー』における映像制作のさらなる高品質化へ!~ 豊富な素材出力と制作フローの改善を実現するツールについて~
cygames
PRO
0
680
【CEDEC2025】LLMを活用したゲーム開発支援と、生成AIの利活用を進める組織的な取り組み
cygames
PRO
1
5k
Other Decks in Technology
See All in Technology
AI駆動開発を通して感じた、 AI時代のデザイナーの役割変化
whisaiyo
0
250
プロダクト開発から業務改善コンサルまで。事業全体へ「染み出す」ことで広がるエンジニアの可能性
ham0215
0
100
日本 Fintech 未来予測レポート 2027〜2028年(手動編集版)
8maki
0
1.9k
2026TECHFRESH畢業分享會 - AI 時代的人生存檔點
line_developers_tw
PRO
0
820
小さくはじめるSLI/SLO ~育てながら組織に定着させる実践知~ / Starting Small with SLI/SLOs: Building Adoption Through Continuous Growth
nari_ex
5
1.8k
Snowflakeと仲良くなる第一歩
coco_se
4
430
就職⽀援サービスにおけるキャリアアドバイザーのシフトスケジューリング
recruitengineers
PRO
1
140
Socrates × Looker 〜セマンティックレイヤーで進化するデータ分析エージェント〜
hanon52_
3
2.1k
AIソロプレナー時代に2ヶ月で20人増員した事業創造会社の開発組織の話
miyatakoji
0
600
小さく始める AI 活用推進 ― 日経電子版 Web チームの事例/nikkei-tech-talk47
nikkei_engineer_recruiting
0
230
2026.06.13_AI時代に事業会社が「SIer出身エンジニア」を求める理由 / Why Businesses Seek Engineers with a System Integrator Background in the AI Era
jumtech
0
1.1k
AmazonRoute 53ではじめてのドメイン取得!HTTPS化までの道のりを整理してみた
usanchuu
3
130
Featured
See All Featured
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
ラッコキーワード サービス紹介資料
rakko
1
3.6M
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
610
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
200
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.3k
Evolution of real-time – Irina Nazarova, EuRuKo, 2024
irinanazarova
9
1.4k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
234
17k
Java REST API Framework Comparison - PWX 2021
mraible
34
9.4k
HTML-Aware ERB: The Path to Reactive Rendering @ RubyCon 2026, Rimini, Italy
marcoroth
1
180
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2k
Claude Code のすすめ
schroneko
67
230k
Transcript
1/92
2/92
3/92 ◆ ◆ ◆
4/92
5/92
6/92
7/92
8/92
9/92
10/92
11/92
12/92
13/92
14/92
15/92
16/92 ◆ ◆
17/92
18/92 ◆ ◆ ◆
19/92
20/92 autoplay_pipeline.exe ( golang ) Jenkins ( groovy Pipeline Script
) granblue_fantasy_relink.exe
21/92
22/92 ◆ ◆ ◆
23/92
24/92
25/92
26/92
27/92
28/92
29/92
30/92
31/92
32/92
33/92
34/92
35/92
36/92
37/92
38/92
39/92
40/92
41/92
42/92
43/92 ◆ ◆
44/92
45/92
46/92
47/92
48/92
49/92
50/92
51/92
52/92
53/92
54/92
55/92 ◆ ◆ ◆
56/92
57/92
58/92
59/92
60/92 ◆ ◆ ◆ ◆
61/92
62/92 f32 PointLightData::updateDistanceCurve() { auto retRate = 0.0f; auto distanceMin
= distanceA_; auto distanceMax = distanceB_; auto t = (cameraDistance_ - distanceMin) / (distanceMax - distanceMin); t = GET_CLAMPF(t, 0.f, 1.f); // return t; }
63/92 f32 PointLightData::updateDistanceCurve() { auto retRate = 0.0f; auto distanceMin
= distanceA_; auto distanceMax = distanceB_; // . auto denominator = (distanceMax - distanceMin); auto t = denominator <= FLT_EPSILON ? 1.f : (cameraDistance_ - distanceMin) / denominator; t = GET_CLAMPF(t, 0.f, 1.f); // return t; }
64/92
65/92 ◆ ◆ ◆ ◆
66/92 Matrix44 transform[3]; // Vector4 vertexs[16]; for (s32 i =
0, inum = std::size(vertexs); i < inum; ++i) { const div_t d = div(i, 4); VecMulMtx(vertexs[i], g_baseVertex[d.rem], transform[d.quot]); }
67/92 Matrix44 transform[4]; // Vector4 vertexs[16]; for (s32 i =
0, inum = std::size(vertexs); i < inum; ++i) { const div_t d = div(i, 4); VecMulMtx(vertexs[i], g_baseVertex[d.rem], transform[d.quot]); }
68/92 constexpr MaterialVariationID DEFAULT_MATERIAL_VARIATION_ID = 0; void hoge() { huga
= KeyData::getConstantKeyParamWithDefault(subseq.materialVariationIdList_, 0.0f, asset::DEFAULT_MATERIAL_VARIATION_ID); } template<typename KeyParam> KeyParam getConstantKeyParamWithDefault(const std::vector<Timeline::EventKeyParamData<KeyParam>>& list, f64 nowSeconds, const KeyParam& defaultValue) { // }
69/92 constexpr MaterialVariationID DEFAULT_MATERIAL_VARIATION_ID = 0; void hoge() { huga
= KeyData::getConstantKeyParamWithDefault(subseq.materialVariationIdList_, 0.0f, asset::DEFAULT_MATERIAL_VARIATION_ID); } template<typename KeyParam> KeyParam getConstantKeyParamWithDefault(const std::vector<Timeline::EventKeyParamData<KeyParam>>& list, f64 nowSeconds, const KeyParam defaultValue) { // }
70/92 SceneObject* SceneObject::clone(SceneObject* pParent, bool isRename, bool isEntity) const {
const char* pName; if (isRename != false) { auto name = mName; // pName = name.get(); } else { pName = mName.get(); } auto pNewObject = SceneObject::create(className(), pName, pParent, false); }
71/92 SceneObject* SceneObject::clone(SceneObject* pParent, bool isRename, bool isEntity) const {
SceneObject* pNewObject = nullptr; if (isRename != false) { auto name = name_; // pNewObject = SceneObject::create(className(), name.get(), pParent, false); } else { pNewObject = SceneObject::create(className(), mName.get(), pParent, false); } }
72/92
73/92
74/92
75/92
76/92 void write() { // void* address = reinterpret_cast<void*> (0x0123456789A);
// placement new MyClass* obj = new (address) MyClass(42); } void read() { // void* address = reinterpret_cast<void*> (0x0123456789A); std::cout << reinterpret_cast<MyClass*> (address)->data << std::endl; }
77/92 ================== WARNING: ThreadSanitizer: data race (pid=174) Write of size
8 at 0x0010a48bb360 by thread T11: #0 0x81119cf39 (ThreadSanitizer.sprx+0x64f39) #1 0x26670e46 in Mtx44Copy(Matrix44&, Matrix44 const&) { } #2 0x26670e46 in Matrix44::operator=(Matrix44 const&) { } #3 0x2c23d8b8 in Joint::setWorldMatrix(Matrix44 const&) { } #4 0x2c23d8b8 in JointList::addVecJointMatrix(Joint&, Vector4 const&) { } … Previous read of size 8 at 0x0010a48bb360 by thread T12: )(void*, void*) const { } … #5 0x2c23b98e in Joint::getWorldPos() const { } #6 0x2c22ce88 in ModelImpl::getWorldPos() const { } …
78/92 ================== WARNING: ThreadSanitizer: data race (pid=174) Write of size
8 at 0x0010a48bb360 by thread T11: #0 0x81119cf39 (ThreadSanitizer.sprx+0x64f39) #1 0x26670e46 in Mtx44Copy(Matrix44&, Matrix44 const&) { } #2 0x26670e46 in Matrix44::operator=(Matrix44 const&) { } #3 0x2c23d8b8 in Joint::setWorldMatrix(Matrix44 const&) { } #4 0x2c23d8b8 in JointList::addVecJointMatrix(Joint&, Vector4 const&) { } … Previous read of size 8 at 0x0010a48bb360 by thread T12: )(void*, void*) const { } … #5 0x2c23b98e in Joint::getWorldPos() const { } #6 0x2c22ce88 in ModelImpl::getWorldPos() const { } …
79/92 void Np0100::updateParameter() { Vector4 playerPos = PlayerManager::GetManualPlayerPos(); Vector4 vec
= getPos() - playerPos; f32 length = VecMag(vec); f32 lengthY = vec.y; vec.y = 0.0f; f32 lengthXZ = VecMag(vec); }
80/92 void Np0100::updateParameter() { Vector4 playerPos = PlayerManager::GetManualPlayerPosRef(); Vector4 vec
= getPos() - playerPos; f32 length = VecMag(vec); f32 lengthY = vec.y; vec.y = 0.0f; f32 lengthXZ = VecMag(vec); }
81/92 ================== WARNING: ThreadSanitizer: data race (pid=490) Write of size
8 at 0x00101a3a4000 by thread T10: #0 0x73b877da in model::bool_vector::set(unsigned long, bool) {コードの場所} #1 0x73bbfcdf in model::BoundingVolume::setForceVisible(bool) const {コードの場所} #2 0x73bfc74b in ModelImpl::updateBoundingVolume() {コードの場所} #3 0x73c24800 in auto ModelProcessingImpl::updateModelList(concurrency::JobSystem&):: ...template... const {コード の場所} #4 0x73c24800 in void concurrency::JobSystem::parallelForEach ...template... const {コードの場所} Previous write of size 8 at 0x00101a3a4000 by thread T14: #0 0x73b877bc in model::bool_vector::set(unsigned long, bool) {コードの場所} #1 0x73bbfcdf in model::BoundingVolume::setForceVisible(bool) const {コードの場所} #2 0x73bfc74b in ModelImpl::updateBoundingVolume() {コードの場所} #3 0x73c24800 in auto ModelProcessingImpl::updateModelList(concurrency::JobSystem&):: ...template... const {コード の場所} #4 0x73c24800 in void concurrency::JobSystem::parallelForEach ...template... const {コードの場所}
82/92 int main() { std::vector<u8> numbers = { 1, 2,
3 }; std::thread t1([&]() { numbers[0] = 42; //OK }); std::thread t2([&]() { numbers[1] = 84; //OK }); t1.join(); t2.join(); return 0; } int main() { std::vector<u8> numbers = { 1, 2, 3 }; std::thread t1([&]() { numbers[0] = 42; //t2 }); std::thread t2([&]() { numbers[0] = 84; //t1 }); t1.join(); t2.join(); return 0; }
83/92 int main() { std::vector<bool> flags = { true, false,
true }; std::thread t1([&]() { flags[0] = true; //NG }); std::thread t2([&]() { flags[1] = true; //NG }); t1.join(); t2.join(); return 0; }
84/92
85/92
86/92
87/92 ◆ ◆ ◆
88/92
89/92 ◆ ◆ ◆
90/92 ◆ ◆ ◆
91/92 ◆ ◆ ◆
92/92