In base we can paste together the relevant columns, convert them to character, and then get the integer code of the factor:
as.numeric(as.factor(paste(df$x,'_',df$y)))
for the data above it is half the speed of the dplyr solution (unclear how it will scale):
microbenchmark::microbenchmark(as.numeric(as.factor(paste(z$x,'_',z$y))), group_indices(df, x, y))
Unit: microseconds
expr min lq mean median uq max neval cld
as.numeric(as.factor(paste(df$x, "_", df$y))) 150.913 153.9855 162.5637 159.745 165.8890 258.817 100 a
group_indices(df, x, y) 322.945 327.3610 339.4574 337.922 340.4175 567.938 100 b