質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

LINQ

LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。

Q&A

解決済

2回答

1176閲覧

[Unity][C#][Linq]ゲーム中にプレイヤー達のそれぞれのスコア(キル数)をランキング形式で表示したい

omaetoomae

総合スコア41

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

LINQ

LINQとはLanguage INtegrated Queryの略で、「統合言語クエリ」という意味です。C#やVisual Basicといった言語のコード内に記述することができるクエリです。

0グッド

0クリップ

投稿2022/05/14 07:25

編集2022/05/15 04:08

内容

Unityにて、ゲーム中にプレイヤーたちの「名前」「キル数」を降順でランキング形式で表示したいです。
※イメージ↓
イメージ説明
コード↓(こちらを参考にさせていただきました)

Scoreboard.cs

1using System.Collections; 2using System.Collections.Generic; 3using System; 4using UnityEngine; 5using UnityEngine.UI; 6using System.Linq; 7 8public class Scores{ 9 public string Name { get; set;} 10 public int Kill { get; set;} 11} 12 13public class Scoreboard : MonoBehaviour 14{ 15 public List<string> namesList = new List<string>(); //後でほかのオブジェクトからリストに追加されていく 16 public Text playernames_text; //名前テキスト 17 public Text playerkills_text; //キル数テキスト 18 public Text playerRank_text; //ランクテキスト 19 20 void Update(){ 21 var scores = new Scores[]{ 22 }; 23 foreach(string name in namesList){ 24 new Scores() 25 { 26 Name = name, 27 Kill = 0, 28 }; 29 } 30 //↑ダメ元でやってみたけどやっぱり無理だった。(そもそも表示されない) 31 var ranking = from s1 in scores 32 let higher = from s2 in scores // 現在の生徒より点数が良いプレイヤーを取得 33 where s2.Kill > s1.Kill 34 select s2 35 select new 36 { 37 s1.Name, 38 s1.Kill, 39 Rank = higher.Count() + 1, // 現在の生徒より点数が良い生徒の人数 + 1 が順位 40 } into s3 41 orderby s3.Rank // 順位で並び替え 42 select s3; 43 44 foreach(var scoreboard in ranking) 45 { 46 playerRank_text.text = scoreboard.Rank +"\n"; //改行 47 playernames_text.text = scoreboard.Name +"\n"; //改行 48 playerkills_text.text = scoreboard.Kill +"\n"; //改行 49 } 50 } 51}

Player.cs

1using System; 2using System.Collections; 3using System.Collections.Generic; 4using UnityEngine; 5 6public class Player : MonoBehaviour 7{ 8 public string name; 9 public int kill = 0; //キル数 10 11 public Scoreboard scoreboard_script; 12 13 void Start () 14 { 15 //スコア 16 name = "a"; //とりあえず"a"とする 17 scoreboard_script.namesList.Add(name); //Scoreboard.csのnamesListに追加する 18 } 19}

今日「Linq」というものを知ったため、そもそもの書き方が間違っているかもしれません。
参考にしたサイトのように書くと以下↓のコードになりますが、これだと後でプレイヤーが増減したときに変更できないし...
※ちなみにUnityには表示されました(おかしいけど)

Scoreboard.cs

1 var scores = new Scores[]{ 2 new Scores(){ 3 Name = "a", 4 Kill = 0, 5 }, 6 new Scores() 7 { 8 Name = "b", 9 Kill = 1, 10 }, 11 };

イメージ説明

どうすればうまく動作するのか、ご教授頂きたいです。

もし、ほかに実装できる方法があれば、それも合わせてご教授頂きたいです。

※抜けている箇所などがあれば修正または追記しますので、教えていただけるとありがたいです。

追記1

Player.csを追加しました。

追記2

"「長さが0の配列」になっている"との指摘を頂いたため、コードを書き換えたのですが、エラーが発生してしまいました。

Scoreboard.cs

1 var scores = new Scores[namesList.Count]; //配列の長さを名前リストと同じ長さにする 2 scores = namesList.ToArray(); //リストから配列に代入 3 //error CS0029: Cannot implicitly convert type 'string[]' to 'Scores[]' 4 foreach(var name in namesList){ 5 new Scores() 6 { 7 Name = name, 8 Kill = 0, 9 }; 10 }

エラー文

Console

1error CS0029: Cannot implicitly convert type 'string[]' to 'Scores[]'

"stringのリストは変換できません"みたいなことが書かれていますが、ではどうしたらいいのかさっぱりわかりません...
こちらのエラーについても、解決いただきたいです。

追記3

fiveHundredさんに教えてもらったようにFor文を使って追加するように書き換えましたが、For文の中でそれぞれのプレイヤーの名前は取得できているものの、うまくScoresに代入できていないようでして...
今一度解説をお願いしたいです。たくさんの追記申し訳ないです...

Scoreboard.cs

1 var scores = new Scores[namesList.Count]; 2 for (int i = 0; i < namesList.Count; i++){ 3 new Scores(){ 4 Name = namesList[i], 5 Kill = 0, 6 }; 7 };

実行したときのエラー文

Console

1NullReferenceException: Object reference not set to an instance of an object 2Scoreboard+<>c.<FixedUpdate>b__6_1 (<>f__AnonymousType0`2[<s1>j__TPar,<higher>j__TPar] <>h__TransparentIdentifier0) (at Scoreboard.cs:69) 3 4System.Linq.Utilities+<>c__DisplayClass2_0`3[TSource,TMiddle,TResult].<CombineSelectors>b__0 (TSource x) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0) 5System.Linq.Enumerable+SelectArrayIterator`2[TSource,TResult].ToArray () (at <351e49e2a5bf4fd6beabb458ce2255f3>:0) 6System.Linq.Buffer`1[TElement]..ctor (System.Collections.Generic.IEnumerable`1[T] source) (at <351e49e2a5bf4fd6beabb458ce2255f3>:0) 7System.Linq.OrderedEnumerable`1+<GetEnumerator>d__3[TElement].MoveNext () (at <351e49e2a5bf4fd6beabb458ce2255f3>:0) 8Scoreboard.Update () (at Scoreboard.cs:78)

Scoreboard.cs

1//以下エラーの場所 2 var ranking = from s1 in scores 3 let higher = from s2 in scores // 現在のプレイヤーより点数が良いプレイヤーを取得 4 where s2.Kill > s1.Kill 5 select s2 6 select new //NullReferenceException:~ 7 { 8 s1.Name, 9 s1.Kill, 10 Rank = higher.Count() + 1, // 現在のプレイヤーより点数が良いプレイヤーの人数 + 1 が順位 11 } into s3 12 orderby s3.Rank // 順位で並び替え 13 select s3;

追記4(解決しました!!!)

fiveHundredさんに指摘された通り、代入がされていませんでしたが、追記のコード通りに書いたところ、見事に上手く動作しました!

↓早速作ったBotを入れて試してみました!
イメージ説明
↓プレイヤーがBotをキルすると...
イメージ説明
キル数が多い順にソートされた!!!(ちょっと不格好ですが...)

最後に

今回の不具合の原因は完全に私が名前を覚えたばっかりの「Linq」を全く学ばずにUnityに実装しようとしたことが原因です。
次回から新しいものに手を出す場合は、少しは基礎知識をつけたうえでコードを書いていこうと思います。

回答してくれた方々、どうもありがとうございました!

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2022/05/14 07:45

> 参考にしたサイトのように書くと以下↓のコードになりますが、これだと後でプレイヤーが増減したときに変更できないし... ハードコーディングしないで済む方法(何らかのデータから動的に作成する方法)はあると思いますので それを考えれば良いのでは? 「//後でほかのオブジェクトからリストに追加されていく」とか書いてありますが、それ以上のことは第三者には分からないので、どう実装するか質問者さんが考える他なさそうです。
omaetoomae

2022/05/14 08:13

ご指摘ありがとうございます。
退会済みユーザー

退会済みユーザー

2022/05/14 23:21 編集

その返事ではどうするのか分かりませんが? 「//後でほかのオブジェクトからリストに追加されていく」と書かれても第三者には分からないので、元データ (from s1 in scores の scores のこと) の具体的サンプルを示してもらわないと話が進まないのですよ。
guest

回答2

0

質問のコメントに書きましたが、「//後でほかのオブジェクトからリストに追加されていく」と書かれても第三者には分からないので、元データ (from s1 in scores の scores のこと) の具体的サンプルを示してもらわないと話が進まないのですよ。

「追記2」で Searchboard.cs を追記してそこに配列 Scores[] を作成するコードを書いたようですが、元になる nameList の中身が不明だし、Kill = 0 に固定したのでは並べ替えのサンプルとして不適です。

例えば、質問の画像のように Name を "a" ~ "j" としてそれから nameList を作り、それをベースに from s1 in scores の scores を生成し、生成する際各 Name の Kill には Name 毎に異なる値を入れておくというようなサンプルを作らないと話が進みません。

それから、scores を Kill で降順に並べ替えて連番を振るという Linq 式ですが、参考にしてるサイトの記事のやり方は間違ってはいないかもしれませんが感心しません。

IEnumerable<T> インターフェイスの Select 拡張メソッドのオーバーロードの一つに Select<TSource,TResult>(IEnumerable<TSource>, Func<TSource,Int32,TResult>) があって、それを使えば 0 番から始まる連番の index を取得することができます。具体例は以下の記事を見てください。

Entity Framework で ROW_NUMBER
http://surferonwww.info/BlogEngine/post/2021/09/20/row-number-in-entity-framework.aspx

上記を考慮に入れたサンプルコードを載せておきます。

using System; using System.Collections.Generic; using System.Linq; namespace ConsoleAppLinq { public class Scores { public string Name { get; set; } public int Kill { get; set; } } internal class Program { static void Main(string[] args) { // 「後でほかのオブジェクトからリストに追加されていく」そうだがそれをここでやる List<string> namesList = new List<string> { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j" }; // namesList から Scores クラスのリストを作る。配列にせず List<Scores> とした。 // Kill のデータはどこから持ってくるのか不明だが、値を入れないと話ができない // のでサンプルとしてランダムな値を入れておく var scores = new List<Scores>(); var rand = new Random(); foreach (string name in namesList) { var score = new Scores { Name = name, Kill = rand.Next(1, 100) }; scores.Add(score); } // scores を Kill で降順に並べ替えて連番を振る。結果は匿名クラスのリストに格納 var sortedList = scores .OrderByDescending(s => s.Kill) .Select((a, index) => new { Rank = index + 1, Name = a.Name, Kill = a.Kill }); // 結果の表示 foreach (var item in sortedList) { Console.WriteLine($"Rank: {item.Rank}, Name: {item.Name}, Kill: {item.Kill}"); } } } }

結果は以下の通り。Kill = rand.Next(1, 100) で値を入れているので結果はアプリの実行のたび違います。

イメージ説明

投稿2022/05/15 01:40

編集2022/05/15 03:02
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

omaetoomae

2022/05/15 04:13

回答ありがとうございます。 無事解決しました。 次回からはもっと質問内容を分かりやすくまとめて質問しようと思います。 何度もご指摘していただきありがとうございました。
guest

0

ベストアンサー

C#

1 var scores = new Scores[]{ 2 }; 3 foreach(string name in namesList){ 4 new Scores() 5 { 6 Name = name, 7 Kill = 0, 8 }; 9 }

上記でScoresを生成しておきながら、どこにも代入されていません。
よって、配列が空のままなので、動かないのは当然です。

また、scoresもこのままだと「長さが0の配列」になっております。
配列は長さを途中で変更することはできません。
初期化時に長さを指定するか、Listを使ってください。


追記:

C#

1 scores = namesList.ToArray(); //リストから配列に代入

上記ではscoresはScoresの配列なのに対し、namesList.ToArray()はstringの配列です。
したがって、scoresに代入できないのは当然です。

そして、

C#

1 foreach(var name in namesList){ 2 new Scores() 3 { 4 Name = name, 5 Kill = 0, 6 }; 7 }

上記、「どこにも代入されていません」って言っているのに全く改善されていません。

配列でやる場合には、for文でループさせ、scores[~]に(各要素ごとに)Scoresを代入させます。
リストであれば、~.Add(~);でリストに追加させます。


追記2:

まだ出来ていません。
new Scores()自体がどこにも代入されていないです。

以下のようにする必要があります。

C#

1 var scores = new Scores[namesList.Count]; 2 for (int i = 0; i < namesList.Count; i++){ 3 scores[i] = new Scores(){ // ←scoresに代入 4 Name = namesList[i], 5 Kill = 0, 6 }; 7 };

投稿2022/05/14 08:40

編集2022/05/15 00:58
fiveHundred

総合スコア9805

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

omaetoomae

2022/05/14 09:59

回答ありがとうございます。 追記したように、回答者さんの指摘されたようにコードを変更してみましたが、別の不具合が発生してしまいました。一度目を通していただけるとありがたいです。
fiveHundred

2022/05/14 10:27

回答に追記しましたので、確認をお願いします。
omaetoomae

2022/05/14 14:15

回答者さんに教えてもらったようにFor文にてScoresクラスに代入を試みましたが、For文の中でそれぞれのプレイヤーの名前は取得できているものの、うまくScoresクラスに代入ができていないようでして... 内容は追記しましたが、今一度説明をしていただきたいです。分からず屋で申し訳ありません...
fiveHundred

2022/05/15 00:58

再度追記しましたので、確認をお願いします。
omaetoomae

2022/05/15 04:11

何度も回答ありがとうございます。 無事解決しました! 私がまだ「Linq」を全く知らずにUnityに実装したがために回答者さんに何度も追記をしていただくことになってしまい誠に申し訳ありませんでした。 本当に、お世話になりました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問