Your question has two unrelated requirements:
There are two ways to address both requirements:
Solution A: If the custom column names are needed for display only:
Pipe the $roleMembers hashtable from Esperento57's answer to Format-Table and provide the desired column names via calculated properties, which in this case effectively rename the hashtable entries' Key property to Roles, and Value to Members:
PS> $roleMembers | Format-Table @{ n='Role'; e='Key' }, @{ n='Members'; e='Value' }
Role  Members
----  -------
role1 member1,member2
role2
role3 member3
role4 member4
Note that Format-Table output, as with all Format-* cmdlets, is suitable for display only, not for later programmatic processing.
Solution B: If you want to keep objects around with properties named Role and Members for later processing:
As suggested by TheMadTechnician, combine the two solutions by making the hashtable values contain custom objects rather than just the list of members:
$roles = 'role1;role2;role3;role4' -split ';'
$members = 'member1,member2;;member3;member4' -split ';'
$rolesAndMembers = [ordered] @{}
foreach ($i in 0..($roles.Count-1)) {
  $rolesAndMembers[$roles[$i]] = [pscustomobject] @{
    Role = $roles[$i]
    Members = $members[$i]
  }
}
# Outputting .Values enumerates the custom objects stored in the hashtable.
# Because the custom objects have fewer than 5 properties, Format-Table is
# implied for output formatting, and the custom objects' property names
# are used as column headers.
$rolesAndMembers.Values
This yields the same output as above.
Note that the implication is that in order to access, say, role1's members, you'll have to use:
$rolesAndMembers.role1.Members # -> 'member1,member2'
# or: $rolesAndMembers['role1'].Members
As an aside: if you wanted to store members individually rather than as  a string list, -split them on custom-object creation:
[pscustomobject] @{
    Role = $roles[$i]
    Members = $members[$i] -split ','  # Members becomes an array with member names
}
$rolesAndMembers.role1.Members[0] would then yield member1, for instance, and $rolesAndMembers.role1.Members[1] would yield member2.
As for what you tried:
$members[($i*2)..($i*2+1)] accesses 2 array elements at an index that is twice that of $i.
However, your $roles and $members arrays have a one-to-one correspondence: they have the same number of elements and for all indices the values directly relate to each other; therefore $members[$i] must be used.