🐥

複数条件でデータをソートする[Unity C#]

2025/02/04に公開

はじめに

自作クラスのListを複数条件でソートする方法についてです。
例えば、装備品をID、レベル、入手順の3項目を考慮しながら並び替えたい、といったときに使えるかもしれません。

補足

今回はLINQを用いずに実装しますが、個人的にはLINQを使った並び替えが一般的なのかなと思ったり。
(何が一般的なのかはあまり詳しくないです。)
下記の記事で実装方法の記載があるので、参考までに。
https://qiita.com/NodeSleepRest/items/4408feb37f36411ffa7e

また、今回は以下のアセットをお借りしています。
https://assetstore.unity.com/packages/2d/gui/icons/rpg-inventory-icons-56687?clickref=1011lAckDV3V&utm_source=partnerize&utm_medium=affiliate&utm_campaign=unity_affiliate

https://assetstore.unity.com/packages/2d/gui/icons/139-vector-icons-69968

やること

所持している武器アイコンを並び替えてみます。
武器は以下のようなデータを持っています。

  • ID (剣:1 斧:2 弓:3)
  • レベル
  • お気に入り設定(ロック機能)

ですが、画像の通り秩序なく並べられてしまっています。
これをゲームらしく、ソートしていきます。

ソートの優先度のルールは以下。

  1. お気に入り設定されているものを優先する。
  2. お気に入り設定されているものが複数あれば、ID順に並べる。
    (設定されていないものについても同様にID順に並べる。)
  3. 同じIDのものが複数あれば、レベル順に並べる。

実装

武器データクラス

WeaponData.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//
// @ 武器のデータクラス
//
public class WeaponData
{
    //
    protected int _id;

    //
    protected int _level;

    //
    protected int _getIndex;

    //
    protected bool _isFavorite;

    //
    public WeaponData( int id, int level, int getTime, bool isFavorite )
    {
        _id = id;
        _level = level;
        _getIndex = getTime;
        _isFavorite = isFavorite;
    }

    //
    // @ ソート用の値を生成
    //
    public int GenerareSortKey()
    {
        int favoriteKey = Convert.ToInt32(_isFavorite) * 10000;
        int idKey = _id * 100 * -1;
        int levelKey = _level; 

        return  favoriteKey + idKey + levelKey;
    }
}

ソート処理

WeaponListView.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WeaponListView : MonoBehaviour
{
    //
    private void Awake()
    {
        var weaponDataList = new List<WeaponData>() 
        {
            new WeaponData( 1, 1, 1, false ),
            new WeaponData( 2, 10, 2, true ),
            new WeaponData( 3, 10, 3, true ),
            new WeaponData( 2, 1, 4, false ),
            new WeaponData( 1, 10, 5, false ),
            new WeaponData( 2, 2, 6, true ),
            new WeaponData( 1, 10, 7, false ),
            new WeaponData( 3, 1, 8, false ),
            new WeaponData( 3, 20, 9, false ),
            new WeaponData( 1, 5, 10, true ),
        };

        // ソート
        weaponDataList.Sort((a, b) => b.GenerareSortKey() - a.GenerareSortKey());

        // アイコンの生成
        // 省略
    }
}

簡単な解説

「ソート用の識別値」を生成し、それをもとにソートを行う手法を取ります。
これによって、ソート部分のコードを簡略化することができます。

WeaponListView.cs
  // ソート
    weaponDataList.Sort((a, b) => b.GenerareSortKey() - a.GenerareSortKey());

ソート処理自体は上記の1行で行っています。
a.GenerateSortKey() でWeaponDataクラスのGenerateSortKey関数にアクセスします。
この関数はintを返すので、その値をキーとしてソートをかけています。

WeaponData.cs
    //
    // @ ソート用の値を生成
    //
    public int GenerareSortKey()
    {
        int favoriteKey = Convert.ToInt32(_isFavorite) * 10000;
        int idKey = _id * 100 * -1;
        int levelKey = _level; 

        return  favoriteKey + idKey + levelKey;
    }

ソート用の識別値を生成する関数です。
複数の変数を数値化して合算、一つの値にしてしまいます。


重要なのは、変数ごとに桁数を決めてしまうことです。
10000や100を乗算しているのは、桁数をずらすためです。
桁数をずらさずにただ足してしまうと、数値の組み合わせによっては全然違うデータなのに同値になってしまう可能性があります。

値の生成方法は、ソートでの優先度が高いものを大きい桁数に持ってきます。
また、降順昇順を反転させたい場合は -1 をかけてください。

これで次の画像のようにソートすることができます。

終わりに

今回の方法はkeyの桁数が膨大になりかねないので、相応しくない場面もあると思います。
(そもそもIDが膨大な数存在するなど)

なのであくまでアイデア程度に。
何かしらの参考になれば幸いです。

Discussion