WP_User_Query order by meta_key that is an array

You could save the values with the separate user meta keys:

x1, ..., x6, points

instead of the array:

achievements

to simplify your WP_User_Query() usage and not having to deal with serialized arrays as user meta values.

But I guess you’ve already considered this setup and wanted to avoid it 😉

A compromise would be to move only the points out of the achievements array into a separate meta key.

Update:

When the number of users is relatively small, we can use the following (untested) PHP sorting:

$users2points = array();

$args = array(
    'meta_key' => 'achievements',
    'number'   => $no,
    'offset'   => $offset
);

$users = get_users( $args );

foreach( (array) $users as $user )
{
    $meta = get_user_meta( 'achievements', $user->ID );
    if( isset( $meta['points'] )
        $users2points[$user->ID] = $meta['points'];
}

asort( $users2points, SORT_NUMERIC );

print_r( $users2points );

Update 2:

Here’s another more adventures PHP sorting that will work directly on the $users array of WP_User objects:

$args = array(
    'meta_key' => 'achievements',
    'number'   => $no,
    'offset'   => $offset
);

$users = get_users( $args );

foreach( (array) $users as $user )
{
    $meta = get_user_meta( 'achievements', $user->ID );

    // Here we assume the points values are greater or equal to 0:
    $user->points = ( isset( $meta['points'] ) ?  $meta['points'] : 0;
}

/*
usort( $users, function( $a, $b ) {
    return gmp_cmp( $a->points, $b->points );
});
*/

// The gmp_cmp() might suffer from a memory leak,
// according to some PHP bug reports,
// so let's use this instead:    

usort( $users, function( $a, $b ) {
    if( $a->points === $b->points )
        return 0;
    else
        return ( $a->points > $b->points ) ? 1 : -1;
});


print_r( $users );